configorama 0.9.12 → 0.9.14
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 +2067 -392
- package/cli.js +47 -9
- package/index.d.ts +1 -0
- package/package.json +1 -17
- package/src/main.js +39 -27
- package/src/parsers/index.js +3 -1
- package/src/parsers/markdown.js +69 -0
- package/src/parsers/markdown.test.js +132 -0
- package/src/resolvers/valueFromEnv.js +3 -6
- package/src/resolvers/valueFromFile.js +4 -4
- package/src/resolvers/valueFromGit.js +128 -86
- package/src/resolvers/valueFromNumber.js +10 -1
- package/src/resolvers/valueFromOptions.js +3 -7
- package/src/resolvers/valueFromParam.js +2 -1
- package/src/types.d.ts +1 -1
- package/src/utils/handleSignalEvents.js +1 -5
- package/src/utils/lodash.js +91 -37
- package/src/utils/parsing/cloudformationSchema.js +5 -10
- package/src/utils/parsing/getValueAtPath.js +111 -0
- package/src/utils/parsing/getValueAtPath.test.js +152 -0
- package/src/utils/parsing/parse.js +22 -1
- package/src/utils/parsing/preProcess.js +16 -10
- package/src/utils/regex/index.js +6 -9
- package/src/utils/ui/configWizard.js +4 -4
- package/src/utils/validation/warnIfNotFound.js +5 -1
- package/src/utils/variables/cleanVariable.js +1 -24
- package/types/src/main.d.ts +2 -0
- package/types/src/main.d.ts.map +1 -1
- package/types/src/parsers/markdown.d.ts +17 -0
- package/types/src/parsers/markdown.d.ts.map +1 -0
- package/types/src/resolvers/valueFromEnv.d.ts +1 -1
- package/types/src/resolvers/valueFromEnv.d.ts.map +1 -1
- package/types/src/resolvers/valueFromGit.d.ts.map +1 -1
- package/types/src/resolvers/valueFromNumber.d.ts +10 -2
- package/types/src/resolvers/valueFromNumber.d.ts.map +1 -1
- package/types/src/resolvers/valueFromOptions.d.ts.map +1 -1
- package/types/src/resolvers/valueFromParam.d.ts.map +1 -1
- package/types/src/utils/handleSignalEvents.d.ts.map +1 -1
- package/types/src/utils/lodash.d.ts +50 -3
- package/types/src/utils/lodash.d.ts.map +1 -1
- package/types/src/utils/parsing/getValueAtPath.d.ts +18 -0
- package/types/src/utils/parsing/getValueAtPath.d.ts.map +1 -0
- package/types/src/utils/parsing/parse.d.ts.map +1 -1
- package/types/src/utils/parsing/preProcess.d.ts.map +1 -1
- package/types/src/utils/regex/index.d.ts +5 -6
- package/types/src/utils/regex/index.d.ts.map +1 -1
- package/types/src/utils/validation/warnIfNotFound.d.ts +4 -0
- package/types/src/utils/validation/warnIfNotFound.d.ts.map +1 -1
- package/types/src/utils/variables/cleanVariable.d.ts +1 -1
- package/types/src/utils/variables/cleanVariable.d.ts.map +1 -1
- package/src/resolvers/valueFromSelf.js +0 -0
package/README.md
CHANGED
|
@@ -1,102 +1,205 @@
|
|
|
1
1
|
# Configorama
|
|
2
2
|
|
|
3
|
-
Dynamic configuration values with variable support.
|
|
3
|
+
Dynamic configuration values with variable support for yml, json, toml, hcl (Terraform), and other config formats.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Configorama extends your configuration with a powerful variable system that resolves values from CLI options, environment variables, file references, TypeScript/JavaScript files, git data, self-references, conditional expressions, and any custom source you define.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Key Features
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
- Eval expressions
|
|
19
|
-
- If/conditional expressions
|
|
20
|
-
- Async/sync JS functions
|
|
21
|
-
- Filters (experimental)
|
|
22
|
-
- Functions (experimental)
|
|
23
|
-
- Any source you'd like...
|
|
24
|
-
|
|
25
|
-
See [tests](https://github.com/DavidWells/configorama/tree/master/tests) for more examples.
|
|
9
|
+
- **Multiple file formats** - yml, yaml, json, toml, ini, hcl (Terraform), TypeScript, JavaScript, markdown
|
|
10
|
+
- **Rich variable sources** - env vars, CLI flags, file refs, git data, cron expressions, eval/if expressions
|
|
11
|
+
- **Async/sync function execution** - Import and execute JavaScript/TypeScript files with argument passing
|
|
12
|
+
- **Self-referencing** - Reference other values within the same config using dot notation
|
|
13
|
+
- **Custom variable sources** - Pluggable architecture to add your own variable resolvers
|
|
14
|
+
- **Filters and functions** - Transform and combine values with built-in or custom operators
|
|
15
|
+
- **Metadata extraction** - Analyze configs without resolving them, or get full resolution history
|
|
16
|
+
- **Circular dependency detection** - Helpful error messages instead of infinite loops
|
|
17
|
+
- **TypeScript support** - Full type definitions and TypeScript file execution via tsx/ts-node
|
|
26
18
|
|
|
27
19
|
## Table of Contents
|
|
20
|
+
|
|
28
21
|
<!-- doc-gen {TOC} collapse=true collapseText="Click to expand" -->
|
|
29
22
|
<details>
|
|
30
23
|
<summary>Click to expand</summary>
|
|
31
24
|
|
|
32
|
-
- [
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
25
|
+
- [Getting Started](#getting-started)
|
|
26
|
+
- [Installation](#installation)
|
|
27
|
+
- [Quick Start](#quick-start)
|
|
28
|
+
- [Running Examples](#running-examples)
|
|
29
|
+
- [How It Works](#how-it-works)
|
|
30
|
+
- [Resolution Flow](#resolution-flow)
|
|
31
|
+
- [Analyzing Without Resolving](#analyzing-without-resolving)
|
|
32
|
+
- [Getting Metadata](#getting-metadata)
|
|
35
33
|
- [Variable Sources](#variable-sources)
|
|
36
|
-
- [Environment
|
|
37
|
-
- [CLI
|
|
38
|
-
- [Parameter
|
|
39
|
-
- [Self
|
|
40
|
-
- [File
|
|
41
|
-
- [Sync/Async
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
- [TypeScript file references](#typescript-file-references)
|
|
46
|
-
- [Git references](#git-references)
|
|
34
|
+
- [Environment Variables](#environment-variables)
|
|
35
|
+
- [CLI Option Flags](#cli-option-flags)
|
|
36
|
+
- [Parameter Values](#parameter-values)
|
|
37
|
+
- [Self References](#self-references)
|
|
38
|
+
- [File References](#file-references)
|
|
39
|
+
- [Sync/Async File References](#syncasync-file-references)
|
|
40
|
+
- [TypeScript File References](#typescript-file-references)
|
|
41
|
+
- [Terraform HCL Support](#terraform-hcl-support)
|
|
42
|
+
- [Git References](#git-references)
|
|
47
43
|
- [Cron Values](#cron-values)
|
|
48
|
-
- [Eval
|
|
49
|
-
- [If
|
|
50
|
-
- [Filters (
|
|
51
|
-
- [Functions (
|
|
52
|
-
|
|
53
|
-
- [
|
|
54
|
-
- [
|
|
55
|
-
- [
|
|
44
|
+
- [Eval Expressions](#eval-expressions)
|
|
45
|
+
- [If Expressions](#if-expressions)
|
|
46
|
+
- [Filters (Experimental)](#filters-experimental)
|
|
47
|
+
- [Functions (Experimental)](#functions-experimental)
|
|
48
|
+
- [API Reference](#api-reference)
|
|
49
|
+
- [Async API](#async-api)
|
|
50
|
+
- [Sync API](#sync-api)
|
|
51
|
+
- [Analyze API](#analyze-api)
|
|
52
|
+
- [Format Utilities](#format-utilities)
|
|
53
|
+
- [Configuration Options](#configuration-options)
|
|
56
54
|
- [Custom Variable Syntax](#custom-variable-syntax)
|
|
57
55
|
- [allowUnknownVariableTypes](#allowunknownvariabletypes)
|
|
58
56
|
- [allowUnresolvedVariables](#allowunresolvedvariables)
|
|
57
|
+
- [Complete Options Reference](#complete-options-reference)
|
|
58
|
+
- [Custom Variable Sources](#custom-variable-sources)
|
|
59
|
+
- [Variable Source Types](#variable-source-types)
|
|
60
|
+
- [Creating a Custom Resolver](#creating-a-custom-resolver)
|
|
61
|
+
- [CLI Usage](#cli-usage)
|
|
62
|
+
- [Basic Commands](#basic-commands)
|
|
63
|
+
- [Command Options](#command-options)
|
|
64
|
+
- [Examples](#cli-examples)
|
|
65
|
+
- [Testing](#testing)
|
|
66
|
+
- [Running Tests](#running-tests)
|
|
67
|
+
- [Test Structure](#test-structure)
|
|
68
|
+
- [Writing Tests](#writing-tests)
|
|
69
|
+
- [Deployment](#deployment)
|
|
70
|
+
- [Using with Serverless Framework](#using-with-serverless-framework)
|
|
71
|
+
- [Docker Deployment](#docker-deployment)
|
|
72
|
+
- [CI/CD Integration](#cicd-integration)
|
|
73
|
+
- [Troubleshooting](#troubleshooting)
|
|
74
|
+
- [Common Issues](#common-issues)
|
|
75
|
+
- [Debug Mode](#debug-mode)
|
|
76
|
+
- [Circular Dependencies](#circular-dependencies)
|
|
59
77
|
- [FAQ](#faq)
|
|
60
|
-
- [
|
|
61
|
-
- [
|
|
78
|
+
- [Advanced Usage](#advanced-usage)
|
|
79
|
+
- [Multi-Stage Resolution](#multi-stage-resolution)
|
|
80
|
+
- [Function Arguments and Context](#function-arguments-and-context)
|
|
81
|
+
- [Programmatic Usage](#programmatic-usage)
|
|
82
|
+
- [What's New](#whats-new)
|
|
83
|
+
- [Alternative Libraries](#alternative-libraries)
|
|
62
84
|
- [Inspiration](#inspiration)
|
|
63
85
|
|
|
64
86
|
</details>
|
|
65
87
|
<!-- end-doc-gen -->
|
|
66
88
|
|
|
67
|
-
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Getting Started
|
|
92
|
+
|
|
93
|
+
### Installation
|
|
94
|
+
|
|
95
|
+
**As a library dependency:**
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm install configorama
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**As a global CLI tool:**
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install -g configorama
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Quick Start
|
|
68
108
|
|
|
69
|
-
Async API
|
|
109
|
+
**Async API (recommended for most use cases):**
|
|
70
110
|
|
|
71
|
-
```
|
|
111
|
+
```javascript
|
|
72
112
|
const path = require('path')
|
|
73
113
|
const configorama = require('configorama')
|
|
74
114
|
const cliFlags = require('minimist')(process.argv.slice(2))
|
|
75
115
|
|
|
76
116
|
// Path to yaml/json/toml config
|
|
77
117
|
const myConfigFilePath = path.join(__dirname, 'config.yml')
|
|
78
|
-
|
|
118
|
+
|
|
119
|
+
// Execute config resolution asynchronously
|
|
79
120
|
const config = await configorama(myConfigFilePath, { options: cliFlags })
|
|
121
|
+
|
|
80
122
|
console.log(config) // resolved config
|
|
81
123
|
```
|
|
82
124
|
|
|
83
|
-
Sync API
|
|
125
|
+
**Sync API (for synchronous execution contexts):**
|
|
84
126
|
|
|
85
|
-
```
|
|
127
|
+
```javascript
|
|
86
128
|
const path = require('path')
|
|
87
129
|
const configorama = require('configorama')
|
|
88
130
|
const cliFlags = require('minimist')(process.argv.slice(2))
|
|
89
131
|
|
|
90
132
|
// Path to yaml/json/toml config
|
|
91
133
|
const myConfigFilePath = path.join(__dirname, 'config.yml')
|
|
92
|
-
|
|
134
|
+
|
|
135
|
+
// Execute config resolution synchronously
|
|
93
136
|
const config = configorama.sync(myConfigFilePath, { options: cliFlags })
|
|
137
|
+
|
|
94
138
|
console.log(config) // resolved config
|
|
95
139
|
```
|
|
96
140
|
|
|
97
|
-
|
|
141
|
+
**Example configuration file (`config.yml`):**
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
# Environment variable
|
|
145
|
+
apiKey: ${env:API_KEY}
|
|
146
|
+
|
|
147
|
+
# CLI option (e.g., --stage prod)
|
|
148
|
+
environment: ${opt:stage, 'dev'}
|
|
149
|
+
|
|
150
|
+
# Self-reference to other values
|
|
151
|
+
service: my-app
|
|
152
|
+
fullName: ${service}-api
|
|
153
|
+
|
|
154
|
+
# File reference
|
|
155
|
+
secrets: ${file(./secrets.yml)}
|
|
156
|
+
|
|
157
|
+
# Git information
|
|
158
|
+
branch: ${git:branch}
|
|
159
|
+
commit: ${git:sha1}
|
|
160
|
+
|
|
161
|
+
# Conditional logic
|
|
162
|
+
memorySize: ${if(${environment} === 'prod' ? 1024 : 512)}
|
|
163
|
+
|
|
164
|
+
# Nested references
|
|
165
|
+
database:
|
|
166
|
+
host: ${env:DB_HOST, 'localhost'}
|
|
167
|
+
port: ${env:DB_PORT, 5432}
|
|
168
|
+
name: ${service}-${environment}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Running Examples
|
|
98
172
|
|
|
99
|
-
|
|
173
|
+
The project includes example files demonstrating various features:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Clone the repository
|
|
177
|
+
git clone https://github.com/DavidWells/configorama
|
|
178
|
+
cd configorama
|
|
179
|
+
|
|
180
|
+
# Install dependencies
|
|
181
|
+
npm install
|
|
182
|
+
|
|
183
|
+
# Run async API example
|
|
184
|
+
node examples/using-async-api.js --stage prod
|
|
185
|
+
|
|
186
|
+
# Run sync API example
|
|
187
|
+
node examples/using-sync-api.js --stage dev
|
|
188
|
+
|
|
189
|
+
# Run zero-config example
|
|
190
|
+
node examples/zero-config.js
|
|
191
|
+
|
|
192
|
+
# Run TypeScript example
|
|
193
|
+
node examples/typescript/using-typescript.js
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## How It Works
|
|
199
|
+
|
|
200
|
+
### Resolution Flow
|
|
201
|
+
|
|
202
|
+
Configorama creates a dependency graph of your config file and all its dependencies, then resolves values based on their variable sources. The resolution process follows this flow:
|
|
100
203
|
|
|
101
204
|
```mermaid
|
|
102
205
|
flowchart TD
|
|
@@ -110,77 +213,194 @@ flowchart TD
|
|
|
110
213
|
H --> I[Return resolved config]
|
|
111
214
|
```
|
|
112
215
|
|
|
113
|
-
**
|
|
216
|
+
**Resolution process:**
|
|
114
217
|
|
|
115
|
-
|
|
218
|
+
1. **Load** - Read config file from disk or accept JavaScript object
|
|
219
|
+
2. **Parse** - Convert to JavaScript object (format auto-detected by extension)
|
|
220
|
+
3. **Preprocess** - Identify all variables and build dependency graph
|
|
221
|
+
4. **Traverse** - Recursively resolve variables in dependency order
|
|
222
|
+
5. **Post-process** - Apply filters and functions
|
|
223
|
+
6. **Return** - Fully resolved configuration object
|
|
224
|
+
|
|
225
|
+
### Analyzing Without Resolving
|
|
226
|
+
|
|
227
|
+
Analyze config structure and variables without actually resolving them:
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
116
230
|
const result = await configorama.analyze('config.yml')
|
|
117
231
|
|
|
118
232
|
// Returns metadata about variables without resolving them
|
|
119
|
-
console.log(result.originalConfig)
|
|
120
|
-
console.log(result.variables)
|
|
121
|
-
console.log(result.uniqueVariables)
|
|
233
|
+
console.log(result.originalConfig) // Raw config object
|
|
234
|
+
console.log(result.variables) // All variables found
|
|
235
|
+
console.log(result.uniqueVariables) // Variables grouped by name
|
|
122
236
|
console.log(result.fileDependencies) // File references found
|
|
123
237
|
```
|
|
124
238
|
|
|
125
|
-
**
|
|
239
|
+
**Use cases:**
|
|
240
|
+
- Validate config structure before deployment
|
|
241
|
+
- Generate documentation of required environment variables
|
|
242
|
+
- Build dependency graphs for complex configs
|
|
243
|
+
- Audit what external resources a config depends on
|
|
244
|
+
|
|
245
|
+
### Getting Metadata
|
|
126
246
|
|
|
127
|
-
|
|
247
|
+
Resolve config and get detailed metadata about the resolution process:
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
128
250
|
const result = await configorama('config.yml', {
|
|
129
251
|
returnMetadata: true,
|
|
130
|
-
// Example option for ${opt:stage}
|
|
131
252
|
options: { stage: 'prod' }
|
|
132
253
|
})
|
|
133
254
|
|
|
134
255
|
// Returns both resolved config and metadata
|
|
135
|
-
console.log(result.
|
|
136
|
-
console.log(result.
|
|
137
|
-
console.log(result.metadata.variables)
|
|
256
|
+
console.log(result.config) // Fully resolved config
|
|
257
|
+
console.log(result.originalConfig) // Raw config object
|
|
258
|
+
console.log(result.metadata.variables) // Variable info with resolution details
|
|
138
259
|
console.log(result.metadata.fileDependencies) // All file dependencies
|
|
139
|
-
console.log(result.metadata.summary)
|
|
140
|
-
console.log(result.resolutionHistory)
|
|
260
|
+
console.log(result.metadata.summary) // { totalVariables, requiredVariables, variablesWithDefaults }
|
|
261
|
+
console.log(result.resolutionHistory) // Step-by-step resolution for each path
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Metadata structure:**
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
{
|
|
268
|
+
config: { /* resolved config */ },
|
|
269
|
+
originalConfig: { /* raw config */ },
|
|
270
|
+
metadata: {
|
|
271
|
+
variables: [
|
|
272
|
+
{
|
|
273
|
+
variable: '${env:API_KEY}',
|
|
274
|
+
variableType: 'env',
|
|
275
|
+
variableName: 'API_KEY',
|
|
276
|
+
variablePath: 'apiKey',
|
|
277
|
+
defaultValue: null,
|
|
278
|
+
hasDefault: false,
|
|
279
|
+
resolved: true,
|
|
280
|
+
resolvedValue: 'secret-key-123'
|
|
281
|
+
},
|
|
282
|
+
// ... more variables
|
|
283
|
+
],
|
|
284
|
+
summary: {
|
|
285
|
+
totalVariables: 15,
|
|
286
|
+
requiredVariables: 8,
|
|
287
|
+
variablesWithDefaults: 7
|
|
288
|
+
},
|
|
289
|
+
fileDependencies: ['./secrets.yml', './config.ts']
|
|
290
|
+
},
|
|
291
|
+
resolutionHistory: {
|
|
292
|
+
'apiKey': [
|
|
293
|
+
{ step: 1, value: '${env:API_KEY}', type: 'env' },
|
|
294
|
+
{ step: 2, value: 'secret-key-123', resolved: true }
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
}
|
|
141
298
|
```
|
|
142
299
|
|
|
300
|
+
---
|
|
301
|
+
|
|
143
302
|
## Variable Sources
|
|
144
303
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
| opt | ${opt:flag} | CLI option flags |
|
|
149
|
-
| param | ${param:key} | Parameter values |
|
|
150
|
-
| self | ${key} or ${self:key} | Self references |
|
|
151
|
-
| file | ${file(path)} | File references |
|
|
152
|
-
| git | ${git:value} | Git data |
|
|
153
|
-
| cron | ${cron(expr)} | Cron expressions |
|
|
154
|
-
| eval | ${eval(expr)} | Math/logic expressions |
|
|
155
|
-
| if | ${if(expr)} | Conditional expressions |
|
|
304
|
+
Configorama supports multiple variable sources out of the box. All variable syntax follows the pattern `${type:value}` or `${type(value)}`.
|
|
305
|
+
|
|
306
|
+
### Summary Table
|
|
156
307
|
|
|
157
|
-
|
|
308
|
+
| Variable | Syntax | Description | Example |
|
|
309
|
+
|----------|-----------------------|------------------------|---------|
|
|
310
|
+
| env | `${env:VAR}` | Environment variables | `${env:NODE_ENV}` |
|
|
311
|
+
| opt | `${opt:flag}` | CLI option flags | `${opt:stage}` |
|
|
312
|
+
| param | `${param:key}` | Parameter values | `${param:domain}` |
|
|
313
|
+
| self | `${key}` or `${self:key}` | Self references | `${database.host}` |
|
|
314
|
+
| file | `${file(path)}` | File references | `${file(./secrets.yml)}` |
|
|
315
|
+
| text | `${text(path)}` | Raw text file | `${text(./README.md)}` |
|
|
316
|
+
| git | `${git:value}` | Git data | `${git:branch}` |
|
|
317
|
+
| cron | `${cron(expr)}` | Cron expressions | `${cron('every 5 minutes')}` |
|
|
318
|
+
| eval | `${eval(expr)}` | Math/logic expressions | `${eval(10 + 5)}` |
|
|
319
|
+
| if | `${if(expr)}` | Conditional expressions| `${if(x > 5 ? 'yes' : 'no')}` |
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
### Environment Variables
|
|
158
324
|
|
|
159
325
|
Access values from `process.env` environment variables.
|
|
160
326
|
|
|
161
|
-
```
|
|
327
|
+
```yaml
|
|
328
|
+
# Basic env var
|
|
162
329
|
apiKey: ${env:SECRET_KEY}
|
|
163
330
|
|
|
164
|
-
#
|
|
331
|
+
# With fallback default if env var not found
|
|
165
332
|
apiKeyWithFallback: ${env:SECRET_KEY, 'defaultApiKey'}
|
|
333
|
+
|
|
334
|
+
# Common patterns
|
|
335
|
+
nodeEnv: ${env:NODE_ENV, 'development'}
|
|
336
|
+
port: ${env:PORT, 3000}
|
|
337
|
+
debug: ${env:DEBUG, false}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**How it works:**
|
|
341
|
+
- Reads from `process.env` at resolution time
|
|
342
|
+
- Supports default values with comma syntax
|
|
343
|
+
- Throws error if env var not found and no default provided (unless `allowUnresolvedVariables` is set)
|
|
344
|
+
|
|
345
|
+
**CLI usage:**
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
# Set env var then run
|
|
349
|
+
SECRET_KEY=abc123 node app.js
|
|
350
|
+
|
|
351
|
+
# Or export first
|
|
352
|
+
export SECRET_KEY=abc123
|
|
353
|
+
node app.js
|
|
166
354
|
```
|
|
167
355
|
|
|
168
|
-
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
### CLI Option Flags
|
|
169
359
|
|
|
170
360
|
Access values from command line arguments passed via the `options` parameter.
|
|
171
361
|
|
|
172
|
-
```
|
|
362
|
+
```yaml
|
|
173
363
|
# CLI option. Example `cmd --stage dev` makes `bar: dev`
|
|
174
364
|
bar: ${opt:stage}
|
|
175
365
|
|
|
176
366
|
# Composed example makes `foo: dev-hello`
|
|
177
367
|
foo: ${opt:stage}-hello
|
|
178
368
|
|
|
179
|
-
#
|
|
180
|
-
|
|
369
|
+
# With default value. If no --stage flag, uses 'dev'
|
|
370
|
+
environment: ${opt:stage, 'dev'}
|
|
371
|
+
|
|
372
|
+
# Boolean flags
|
|
373
|
+
verbose: ${opt:verbose, false}
|
|
374
|
+
|
|
375
|
+
# Nested paths
|
|
376
|
+
region: ${opt:aws.region, 'us-east-1'}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**How it works:**
|
|
380
|
+
- Reads from the `options` object passed to configorama
|
|
381
|
+
- Typically populated from CLI args using `minimist` or similar parser
|
|
382
|
+
- Supports dot-notation for nested option paths
|
|
383
|
+
|
|
384
|
+
**Example:**
|
|
385
|
+
|
|
386
|
+
```javascript
|
|
387
|
+
const minimist = require('minimist')
|
|
388
|
+
const configorama = require('configorama')
|
|
389
|
+
|
|
390
|
+
const argv = minimist(process.argv.slice(2))
|
|
391
|
+
// argv = { stage: 'prod', verbose: true, aws: { region: 'eu-west-1' } }
|
|
392
|
+
|
|
393
|
+
const config = await configorama('config.yml', { options: argv })
|
|
181
394
|
```
|
|
182
395
|
|
|
183
|
-
|
|
396
|
+
```bash
|
|
397
|
+
# Command line
|
|
398
|
+
node app.js --stage prod --verbose --aws.region eu-west-1
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
### Parameter Values
|
|
184
404
|
|
|
185
405
|
Access parameter values via `${param:key}`. Parameters follow a resolution hierarchy:
|
|
186
406
|
|
|
@@ -188,7 +408,7 @@ Access parameter values via `${param:key}`. Parameters follow a resolution hiera
|
|
|
188
408
|
2. **Stage-specific params** (`stages.<stage>.params`)
|
|
189
409
|
3. **Default params** (`stages.default.params`)
|
|
190
410
|
|
|
191
|
-
```
|
|
411
|
+
```yaml
|
|
192
412
|
# Direct parameter reference
|
|
193
413
|
appDomain: ${param:domain}
|
|
194
414
|
|
|
@@ -219,11 +439,14 @@ node app.js --param="domain=example.com"
|
|
|
219
439
|
|
|
220
440
|
# Multiple params
|
|
221
441
|
node app.js --param="domain=example.com" --param="apiKey=secret123"
|
|
442
|
+
|
|
443
|
+
# With stage selection
|
|
444
|
+
node app.js --stage prod --param="domain=cli-override.com"
|
|
222
445
|
```
|
|
223
446
|
|
|
224
447
|
**Code Usage:**
|
|
225
448
|
|
|
226
|
-
```
|
|
449
|
+
```javascript
|
|
227
450
|
const config = await configorama('config.yml', {
|
|
228
451
|
options: {
|
|
229
452
|
stage: 'prod',
|
|
@@ -232,11 +455,41 @@ const config = await configorama('config.yml', {
|
|
|
232
455
|
})
|
|
233
456
|
```
|
|
234
457
|
|
|
235
|
-
|
|
458
|
+
**Resolution order example:**
|
|
459
|
+
|
|
460
|
+
```yaml
|
|
461
|
+
stages:
|
|
462
|
+
prod:
|
|
463
|
+
params:
|
|
464
|
+
domain: prod.myapp.com # 2. Stage-specific
|
|
465
|
+
default:
|
|
466
|
+
params:
|
|
467
|
+
domain: default.myapp.com # 3. Default fallback
|
|
468
|
+
|
|
469
|
+
appUrl: ${param:domain}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
# CLI override (highest priority)
|
|
474
|
+
node app.js --stage prod --param="domain=cli.myapp.com"
|
|
475
|
+
# Result: appUrl = 'cli.myapp.com'
|
|
476
|
+
|
|
477
|
+
# Stage param (no CLI override)
|
|
478
|
+
node app.js --stage prod
|
|
479
|
+
# Result: appUrl = 'prod.myapp.com'
|
|
480
|
+
|
|
481
|
+
# Default param (no CLI override, no stage match)
|
|
482
|
+
node app.js --stage staging
|
|
483
|
+
# Result: appUrl = 'default.myapp.com'
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
### Self References
|
|
236
489
|
|
|
237
|
-
Reference values from other key paths in the same configuration file.
|
|
490
|
+
Reference values from other key paths in the same configuration file using dot notation.
|
|
238
491
|
|
|
239
|
-
```
|
|
492
|
+
```yaml
|
|
240
493
|
foo: bar
|
|
241
494
|
|
|
242
495
|
zaz:
|
|
@@ -244,35 +497,66 @@ zaz:
|
|
|
244
497
|
wow:
|
|
245
498
|
cool: 2
|
|
246
499
|
|
|
247
|
-
# Shorthand dot.prop reference
|
|
248
|
-
two: ${foo}
|
|
500
|
+
# Shorthand dot.prop reference
|
|
501
|
+
two: ${foo} # Resolves to 'bar'
|
|
249
502
|
|
|
250
|
-
#
|
|
251
|
-
one: ${self:foo}
|
|
503
|
+
# Explicit self file reference
|
|
504
|
+
one: ${self:foo} # Resolves to 'bar'
|
|
252
505
|
|
|
253
|
-
# Dot prop reference
|
|
254
|
-
three: ${zaz.wow.cool}
|
|
506
|
+
# Dot prop reference traverses objects
|
|
507
|
+
three: ${zaz.wow.cool} # Resolves to 2
|
|
508
|
+
|
|
509
|
+
# Complex nested references
|
|
510
|
+
database:
|
|
511
|
+
host: localhost
|
|
512
|
+
port: 5432
|
|
513
|
+
name: mydb
|
|
514
|
+
|
|
515
|
+
connectionString: postgres://${database.host}:${database.port}/${database.name}
|
|
516
|
+
# Resolves to: postgres://localhost:5432/mydb
|
|
517
|
+
|
|
518
|
+
# Array access
|
|
519
|
+
items:
|
|
520
|
+
- first
|
|
521
|
+
- second
|
|
522
|
+
- third
|
|
523
|
+
|
|
524
|
+
selectedItem: ${items[1]} # Resolves to 'second'
|
|
255
525
|
```
|
|
256
526
|
|
|
257
|
-
|
|
527
|
+
**How it works:**
|
|
528
|
+
- Uses dot-notation for nested object access
|
|
529
|
+
- Supports array index access with bracket notation
|
|
530
|
+
- Resolves in dependency order (referenced values resolved first)
|
|
531
|
+
- Detects circular references and throws helpful errors
|
|
258
532
|
|
|
259
|
-
|
|
533
|
+
---
|
|
260
534
|
|
|
261
|
-
|
|
535
|
+
### File References
|
|
536
|
+
|
|
537
|
+
Import values from external yml, json, toml, hcl, or other supported files by relative path.
|
|
538
|
+
|
|
539
|
+
```yaml
|
|
262
540
|
# Import full yml/json/toml/hcl file via relative path
|
|
263
541
|
fileRef: ${file(./subFile.yml)}
|
|
264
542
|
|
|
265
|
-
# Import sub values from files
|
|
543
|
+
# Import sub values from files (topLevel key from other-config.yml)
|
|
266
544
|
fileValue: ${file(./other-config.yml):topLevel}
|
|
267
545
|
|
|
268
|
-
# Import sub values
|
|
546
|
+
# Import nested sub values (nested.value from other-config.json)
|
|
269
547
|
fileValueSubKey: ${file(./other-config.json):nested.value}
|
|
270
548
|
|
|
271
549
|
# Fallback to default value if file not found
|
|
272
550
|
fallbackValueExample: ${file(./not-found.yml), 'fall back value'}
|
|
551
|
+
|
|
552
|
+
# Relative paths from config file location
|
|
553
|
+
secrets: ${file(../shared/secrets.yml)}
|
|
554
|
+
|
|
555
|
+
# Import from subdirectory
|
|
556
|
+
dbConfig: ${file(./config/database.yml):production}
|
|
273
557
|
```
|
|
274
558
|
|
|
275
|
-
Supported file types (extensions are case-insensitive)
|
|
559
|
+
**Supported file types (extensions are case-insensitive):**
|
|
276
560
|
|
|
277
561
|
| Type | Extensions |
|
|
278
562
|
|------|------------|
|
|
@@ -283,32 +567,82 @@ Supported file types (extensions are case-insensitive):
|
|
|
283
567
|
| TOML | `.toml`, `.tml` |
|
|
284
568
|
| INI | `.ini` |
|
|
285
569
|
| JSON | `.json`, `.json5`, `.jsonc` |
|
|
570
|
+
| HCL (Terraform) | `.tf`, `.hcl`, `.tf.json` |
|
|
571
|
+
| Markdown | `.md`, `.mdx`, `.markdown`, `.mdown`, `.mkdn`, `.mkd` |
|
|
572
|
+
|
|
573
|
+
**Path resolution:**
|
|
574
|
+
- Relative paths resolved from config file's directory
|
|
575
|
+
- Absolute paths supported
|
|
576
|
+
- `~` home directory expansion NOT supported (use absolute paths)
|
|
577
|
+
|
|
578
|
+
**Example file structure:**
|
|
579
|
+
|
|
580
|
+
```text
|
|
581
|
+
project/
|
|
582
|
+
├── config.yml # Main config
|
|
583
|
+
├── secrets.yml # Secrets file
|
|
584
|
+
└── environments/
|
|
585
|
+
├── dev.yml
|
|
586
|
+
└── prod.yml
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
```yaml
|
|
590
|
+
# config.yml
|
|
591
|
+
secrets: ${file(./secrets.yml)}
|
|
592
|
+
environment: ${file(./environments/${opt:stage}.yml)}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
---
|
|
286
596
|
|
|
287
|
-
### Sync/Async
|
|
597
|
+
### Sync/Async File References
|
|
288
598
|
|
|
289
|
-
Execute JavaScript files and use their exported function's return value.
|
|
599
|
+
Execute JavaScript files and use their exported function's return value. Functions can be synchronous or asynchronous and receive arguments from your config.
|
|
290
600
|
|
|
291
|
-
```
|
|
601
|
+
```yaml
|
|
602
|
+
# Async function execution
|
|
292
603
|
asyncJSValue: ${file(./async-value.js)}
|
|
293
|
-
|
|
604
|
+
|
|
605
|
+
# Sync function execution
|
|
606
|
+
syncJSValue: ${file(./sync-value.js)}
|
|
607
|
+
|
|
608
|
+
# With arguments (resolved before being passed)
|
|
609
|
+
secrets: ${file(./fetch-secrets.js, ${self:environment}, ${self:region})}
|
|
294
610
|
```
|
|
295
611
|
|
|
296
|
-
|
|
612
|
+
**JavaScript file example (`async-value.js`):**
|
|
297
613
|
|
|
298
|
-
```
|
|
614
|
+
```javascript
|
|
299
615
|
async function fetchSecretsFromRemoteStore() {
|
|
300
|
-
|
|
301
|
-
|
|
616
|
+
// Simulate async operation (AWS Secrets Manager, HashiCorp Vault, etc.)
|
|
617
|
+
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
618
|
+
return {
|
|
619
|
+
apiKey: 'secret-key-123',
|
|
620
|
+
dbPassword: 'db-password-456'
|
|
621
|
+
}
|
|
302
622
|
}
|
|
303
623
|
|
|
304
624
|
module.exports = fetchSecretsFromRemoteStore
|
|
305
625
|
```
|
|
306
626
|
|
|
307
|
-
|
|
627
|
+
**Sync function example (`sync-value.js`):**
|
|
308
628
|
|
|
309
|
-
|
|
629
|
+
```javascript
|
|
630
|
+
function getEnvironmentConfig() {
|
|
631
|
+
return {
|
|
632
|
+
timeout: 5000,
|
|
633
|
+
retries: 3,
|
|
634
|
+
logLevel: process.env.NODE_ENV === 'production' ? 'error' : 'debug'
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
module.exports = getEnvironmentConfig
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
#### Passing Arguments to Functions
|
|
310
642
|
|
|
311
|
-
|
|
643
|
+
You can pass resolved values from your config as arguments to JavaScript/TypeScript functions:
|
|
644
|
+
|
|
645
|
+
```yaml
|
|
312
646
|
foo: bar
|
|
313
647
|
baz:
|
|
314
648
|
qux: quux
|
|
@@ -319,7 +653,7 @@ secrets: ${file(./fetch-secrets.js, ${self:foo}, ${self:baz})}
|
|
|
319
653
|
|
|
320
654
|
Arguments are passed in order, with the config context always last:
|
|
321
655
|
|
|
322
|
-
```
|
|
656
|
+
```javascript
|
|
323
657
|
/**
|
|
324
658
|
* @param {string} foo - First arg from YAML ('bar')
|
|
325
659
|
* @param {object} baz - Second arg from YAML ({ qux: 'quux' })
|
|
@@ -328,6 +662,8 @@ Arguments are passed in order, with the config context always last:
|
|
|
328
662
|
async function fetchSecrets(foo, baz, ctx) {
|
|
329
663
|
console.log(foo) // 'bar'
|
|
330
664
|
console.log(baz) // { qux: 'quux' }
|
|
665
|
+
|
|
666
|
+
// Access config context
|
|
331
667
|
console.log(ctx.originalConfig) // Original unresolved config
|
|
332
668
|
console.log(ctx.currentConfig) // Current partially-resolved config
|
|
333
669
|
console.log(ctx.options) // Options passed to configorama
|
|
@@ -348,7 +684,7 @@ The `ctx` parameter (always the last argument) provides access to:
|
|
|
348
684
|
| `currentConfig` | The current (partially resolved) configuration |
|
|
349
685
|
| `options` | Options passed to configorama (populates `${opt:xyz}` variables) |
|
|
350
686
|
|
|
351
|
-
TypeScript users can import the type
|
|
687
|
+
**TypeScript users can import the type:**
|
|
352
688
|
|
|
353
689
|
```typescript
|
|
354
690
|
import type { ConfigContext } from 'configorama'
|
|
@@ -365,11 +701,11 @@ async function fetchSecrets(
|
|
|
365
701
|
export = fetchSecrets
|
|
366
702
|
```
|
|
367
703
|
|
|
368
|
-
#### Functions
|
|
704
|
+
#### Functions Without Arguments
|
|
369
705
|
|
|
370
706
|
If you don't need arguments, the function still receives `ctx` as its only parameter:
|
|
371
707
|
|
|
372
|
-
```
|
|
708
|
+
```javascript
|
|
373
709
|
// No args - ctx is the only parameter
|
|
374
710
|
async function getSecrets(ctx) {
|
|
375
711
|
return ctx.options.stage === 'prod'
|
|
@@ -380,11 +716,25 @@ async function getSecrets(ctx) {
|
|
|
380
716
|
module.exports = getSecrets
|
|
381
717
|
```
|
|
382
718
|
|
|
383
|
-
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
### TypeScript File References
|
|
384
722
|
|
|
385
723
|
Execute TypeScript files using tsx (recommended) or ts-node.
|
|
386
724
|
|
|
387
|
-
|
|
725
|
+
**Installation:**
|
|
726
|
+
|
|
727
|
+
```bash
|
|
728
|
+
# Recommended: Modern, fast TypeScript execution
|
|
729
|
+
npm install tsx --save-dev
|
|
730
|
+
|
|
731
|
+
# Alternative: Traditional ts-node approach
|
|
732
|
+
npm install ts-node typescript --save-dev
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
**Usage in config:**
|
|
736
|
+
|
|
737
|
+
```yaml
|
|
388
738
|
# TypeScript configuration object
|
|
389
739
|
config: ${file(./config.ts)}
|
|
390
740
|
|
|
@@ -393,52 +743,54 @@ secrets: ${file(./async-secrets.ts)}
|
|
|
393
743
|
|
|
394
744
|
# Specific property from TypeScript export
|
|
395
745
|
database: ${file(./config.ts):database}
|
|
746
|
+
|
|
747
|
+
# With arguments
|
|
748
|
+
apiConfig: ${file(./config.ts, ${opt:stage})}
|
|
396
749
|
```
|
|
397
750
|
|
|
398
|
-
**TypeScript Object Export:**
|
|
751
|
+
**TypeScript Object Export (`typescript-config.ts`):**
|
|
399
752
|
|
|
400
753
|
```typescript
|
|
401
|
-
/* typescript-config.ts */
|
|
402
754
|
interface DatabaseConfig {
|
|
403
|
-
host: string
|
|
404
|
-
port: number
|
|
405
|
-
database: string
|
|
406
|
-
ssl: boolean
|
|
755
|
+
host: string
|
|
756
|
+
port: number
|
|
757
|
+
database: string
|
|
758
|
+
ssl: boolean
|
|
407
759
|
}
|
|
408
760
|
|
|
409
761
|
interface ApiConfig {
|
|
410
|
-
baseUrl: string
|
|
411
|
-
timeout: number
|
|
412
|
-
retries: number
|
|
762
|
+
baseUrl: string
|
|
763
|
+
timeout: number
|
|
764
|
+
retries: number
|
|
413
765
|
}
|
|
414
766
|
|
|
415
767
|
interface ConfigObject {
|
|
416
|
-
environment: string
|
|
417
|
-
database: DatabaseConfig
|
|
418
|
-
api: ApiConfig
|
|
768
|
+
environment: string
|
|
769
|
+
database: DatabaseConfig
|
|
770
|
+
api: ApiConfig
|
|
419
771
|
features: {
|
|
420
|
-
enableNewFeature: boolean
|
|
421
|
-
debugMode: boolean
|
|
422
|
-
}
|
|
772
|
+
enableNewFeature: boolean
|
|
773
|
+
debugMode: boolean
|
|
774
|
+
}
|
|
423
775
|
}
|
|
424
776
|
|
|
425
777
|
function createConfig(): ConfigObject {
|
|
426
778
|
return {
|
|
427
|
-
environment: '
|
|
779
|
+
environment: process.env.STAGE || 'development',
|
|
428
780
|
database: {
|
|
429
|
-
host:
|
|
430
|
-
port: parseInt(
|
|
431
|
-
database:
|
|
432
|
-
ssl:
|
|
781
|
+
host: process.env.DB_HOST || 'localhost',
|
|
782
|
+
port: parseInt(process.env.DB_PORT || '5432'),
|
|
783
|
+
database: process.env.DB_NAME || 'myapp',
|
|
784
|
+
ssl: process.env.NODE_ENV === 'production'
|
|
433
785
|
},
|
|
434
786
|
api: {
|
|
435
|
-
baseUrl:
|
|
787
|
+
baseUrl: process.env.API_BASE_URL || 'http://localhost:3000',
|
|
436
788
|
timeout: 5000,
|
|
437
789
|
retries: 3
|
|
438
790
|
},
|
|
439
791
|
features: {
|
|
440
|
-
enableNewFeature:
|
|
441
|
-
debugMode:
|
|
792
|
+
enableNewFeature: process.env.STAGE === 'production',
|
|
793
|
+
debugMode: process.env.DEBUG === 'true'
|
|
442
794
|
}
|
|
443
795
|
}
|
|
444
796
|
}
|
|
@@ -446,14 +798,13 @@ function createConfig(): ConfigObject {
|
|
|
446
798
|
export = createConfig
|
|
447
799
|
```
|
|
448
800
|
|
|
449
|
-
**TypeScript Async Function:**
|
|
801
|
+
**TypeScript Async Function (`typescript-async.ts`):**
|
|
450
802
|
|
|
451
803
|
```typescript
|
|
452
|
-
/* typescript-async.ts */
|
|
453
804
|
interface SecretStore {
|
|
454
|
-
apiKey: string
|
|
455
|
-
dbPassword: string
|
|
456
|
-
jwtSecret: string
|
|
805
|
+
apiKey: string
|
|
806
|
+
dbPassword: string
|
|
807
|
+
jwtSecret: string
|
|
457
808
|
}
|
|
458
809
|
|
|
459
810
|
function delay(ms: number): Promise<void> {
|
|
@@ -462,10 +813,10 @@ function delay(ms: number): Promise<void> {
|
|
|
462
813
|
|
|
463
814
|
async function fetchSecretsFromVault(): Promise<SecretStore> {
|
|
464
815
|
console.log('Fetching secrets from vault...')
|
|
465
|
-
|
|
466
|
-
// Simulate async operations
|
|
816
|
+
|
|
817
|
+
// Simulate async operations (AWS Secrets Manager, HashiCorp Vault, etc.)
|
|
467
818
|
await delay(100)
|
|
468
|
-
|
|
819
|
+
|
|
469
820
|
return {
|
|
470
821
|
apiKey: process.env.API_KEY || 'dev-api-key',
|
|
471
822
|
dbPassword: process.env.DB_PASSWORD || 'dev-password',
|
|
@@ -478,7 +829,7 @@ export = fetchSecretsFromVault
|
|
|
478
829
|
|
|
479
830
|
**Complete Example Configuration:**
|
|
480
831
|
|
|
481
|
-
```
|
|
832
|
+
```yaml
|
|
482
833
|
# config-with-typescript.yml
|
|
483
834
|
service: my-awesome-app
|
|
484
835
|
|
|
@@ -492,10 +843,10 @@ secrets: ${file(./typescript-async.ts)}
|
|
|
492
843
|
custom:
|
|
493
844
|
stage: ${opt:stage, "dev"}
|
|
494
845
|
region: ${opt:region, "us-east-1"}
|
|
495
|
-
|
|
496
|
-
#
|
|
846
|
+
|
|
847
|
+
# Use TypeScript files for specific sections
|
|
497
848
|
databaseConfig: ${file(./typescript-config.ts):database}
|
|
498
|
-
|
|
849
|
+
|
|
499
850
|
# Environment-specific overrides
|
|
500
851
|
stageVariables:
|
|
501
852
|
dev:
|
|
@@ -506,8 +857,7 @@ custom:
|
|
|
506
857
|
# Regular configuration values
|
|
507
858
|
resources:
|
|
508
859
|
description: "Configuration loaded with TypeScript support"
|
|
509
|
-
|
|
510
|
-
|
|
860
|
+
|
|
511
861
|
functions:
|
|
512
862
|
hello:
|
|
513
863
|
handler: handler.hello
|
|
@@ -517,26 +867,16 @@ functions:
|
|
|
517
867
|
API_KEY: ${self:secrets.apiKey}
|
|
518
868
|
```
|
|
519
869
|
|
|
520
|
-
**Installation Requirements:**
|
|
521
|
-
|
|
522
|
-
TypeScript support requires either `tsx` (recommended) or `ts-node`:
|
|
523
|
-
|
|
524
|
-
```bash
|
|
525
|
-
# Recommended: Modern, fast TypeScript execution
|
|
526
|
-
npm install tsx --save-dev
|
|
527
|
-
|
|
528
|
-
# Alternative: Traditional ts-node approach
|
|
529
|
-
npm install ts-node typescript --save-dev
|
|
530
|
-
```
|
|
531
|
-
|
|
532
870
|
**Features:**
|
|
533
871
|
- Modern tsx execution (fast, no compilation) with ts-node fallback
|
|
534
872
|
- Support for both sync and async TypeScript functions
|
|
535
|
-
- Function argument passing via
|
|
873
|
+
- Function argument passing via config variables
|
|
536
874
|
- Full TypeScript interface support
|
|
537
875
|
- Comprehensive error handling with helpful dependency messages
|
|
538
876
|
|
|
539
|
-
|
|
877
|
+
---
|
|
878
|
+
|
|
879
|
+
### Terraform HCL Support
|
|
540
880
|
|
|
541
881
|
Configorama supports Terraform HCL (HashiCorp Configuration Language) files, allowing you to parse `.tf`, `.tf.json`, and `.hcl` files.
|
|
542
882
|
|
|
@@ -555,7 +895,7 @@ npm install @cdktf/hcl2json
|
|
|
555
895
|
|
|
556
896
|
**Example:**
|
|
557
897
|
|
|
558
|
-
```
|
|
898
|
+
```javascript
|
|
559
899
|
const configorama = require('configorama')
|
|
560
900
|
|
|
561
901
|
// Parse a Terraform configuration file
|
|
@@ -570,7 +910,7 @@ console.log(terraformConfig.output) // Outputs
|
|
|
570
910
|
|
|
571
911
|
**Importing Terraform files:**
|
|
572
912
|
|
|
573
|
-
```
|
|
913
|
+
```yaml
|
|
574
914
|
# Import Terraform variables from a .tf file
|
|
575
915
|
terraformVars: ${file(./terraform/variables.tf)}
|
|
576
916
|
|
|
@@ -579,9 +919,10 @@ region: ${file(./terraform/variables.tf):variable.region[0].default}
|
|
|
579
919
|
```
|
|
580
920
|
|
|
581
921
|
**Variable syntax:**
|
|
922
|
+
|
|
582
923
|
When loading `.tf` or `.hcl` files directly, configorama automatically uses `$[...]` syntax instead of `${...}` to avoid conflicts with Terraform's native `${var.name}` interpolation. Terraform expressions like `${var.environment}` and `${map(string)}` are preserved as-is.
|
|
583
924
|
|
|
584
|
-
```
|
|
925
|
+
```javascript
|
|
585
926
|
// Loading .tf directly - uses $[...] syntax automatically
|
|
586
927
|
const config = await configorama('./main.tf')
|
|
587
928
|
// config.locals[0].app_name = "myapp-${var.environment}" (preserved)
|
|
@@ -593,23 +934,26 @@ const config = await configorama('./main.tf')
|
|
|
593
934
|
|
|
594
935
|
When importing `.tf` files from other config formats (yml, json, etc.) via `${file()}`, the parent file's syntax applies. Use `allowUnknownVariableTypes: true` if the imported `.tf` contains Terraform interpolations:
|
|
595
936
|
|
|
596
|
-
```
|
|
937
|
+
```javascript
|
|
597
938
|
const config = await configorama('./config.yml', {
|
|
598
939
|
allowUnknownVariableTypes: true
|
|
599
940
|
})
|
|
600
941
|
```
|
|
601
942
|
|
|
602
943
|
**Read-only support:**
|
|
944
|
+
|
|
603
945
|
Currently, HCL files can be read and parsed, but writing/generating HCL files is not supported.
|
|
604
946
|
|
|
605
947
|
See [tests/hclTests](./tests/hclTests) for example Terraform files.
|
|
606
948
|
|
|
607
|
-
|
|
949
|
+
---
|
|
950
|
+
|
|
951
|
+
### Git References
|
|
608
952
|
|
|
609
953
|
Access repository information from the current working directory's git data.
|
|
610
954
|
|
|
611
955
|
<!-- doc-gen CODE src=tests/gitVariables/gitVariables.yml -->
|
|
612
|
-
```
|
|
956
|
+
```yaml
|
|
613
957
|
########################
|
|
614
958
|
# Git Variables
|
|
615
959
|
########################
|
|
@@ -661,12 +1005,18 @@ gitTimestampAbsolutePath: ${git:timestamp('package.json')}
|
|
|
661
1005
|
```
|
|
662
1006
|
<!-- end-doc-gen -->
|
|
663
1007
|
|
|
1008
|
+
**How it works:**
|
|
1009
|
+
- Reads git data from `.git` directory in current working directory or parent directories
|
|
1010
|
+
- Executes git commands via child process
|
|
1011
|
+
- Throws error if not in a git repository
|
|
1012
|
+
|
|
1013
|
+
---
|
|
664
1014
|
|
|
665
1015
|
### Cron Values
|
|
666
1016
|
|
|
667
1017
|
Convert human-readable time expressions into standard cron syntax.
|
|
668
1018
|
|
|
669
|
-
```
|
|
1019
|
+
```yaml
|
|
670
1020
|
# Basic patterns
|
|
671
1021
|
everyMinute: ${cron('every minute')} # * * * * *
|
|
672
1022
|
everyHour: ${cron('every hour')} # 0 * * * *
|
|
@@ -679,7 +1029,7 @@ noon: ${cron('noon')} # 0 12 * * *
|
|
|
679
1029
|
every5Minutes: ${cron('every 5 minutes')} # */5 * * * *
|
|
680
1030
|
every15Minutes: ${cron('every 15 minutes')} # */15 * * * *
|
|
681
1031
|
every2Hours: ${cron('every 2 hours')} # 0 */2 * * *
|
|
682
|
-
every3Days: ${cron('every 3 days')} # 0 0 */3 * *
|
|
1032
|
+
every3Days: ${cron('every 3 days')} # 0 0 */3 * *
|
|
683
1033
|
|
|
684
1034
|
# Specific times
|
|
685
1035
|
at930: ${cron('at 9:30')} # 30 9 * * *
|
|
@@ -692,56 +1042,92 @@ mondayMorning: ${cron('on monday at 9:00')} # 0 9 * * 1
|
|
|
692
1042
|
fridayEvening: ${cron('on friday at 17:00')} # 0 17 * * 5
|
|
693
1043
|
sundayNoon: ${cron('on sunday at 12:00')} # 0 12 * * 0
|
|
694
1044
|
|
|
695
|
-
# Pre-existing cron expressions
|
|
1045
|
+
# Pre-existing cron expressions (pass through)
|
|
696
1046
|
customCron: ${cron('15 2 * * *')} # 15 2 * * *
|
|
697
1047
|
```
|
|
698
1048
|
|
|
699
|
-
|
|
1049
|
+
**Supported expressions:**
|
|
1050
|
+
- `every N minutes/hours/days`
|
|
1051
|
+
- `at HH:MM [am/pm]`
|
|
1052
|
+
- `on [weekday] at HH:MM`
|
|
1053
|
+
- `midnight`, `noon`, `weekdays`
|
|
1054
|
+
- Standard cron syntax (passed through unchanged)
|
|
1055
|
+
|
|
1056
|
+
---
|
|
1057
|
+
|
|
1058
|
+
### Eval Expressions
|
|
700
1059
|
|
|
701
|
-
Evaluate mathematical and logical expressions safely (without using JavaScript's `eval`).
|
|
1060
|
+
Evaluate mathematical and logical expressions safely (without using JavaScript's `eval`). Uses the `subscript` library for safe expression evaluation.
|
|
702
1061
|
|
|
703
|
-
```
|
|
1062
|
+
```yaml
|
|
704
1063
|
# Math operations
|
|
705
1064
|
sum: ${eval(10 + 5)} # 15
|
|
706
1065
|
multiply: ${eval(10 * 3)} # 30
|
|
707
1066
|
divide: ${eval(100 / 4)} # 25
|
|
1067
|
+
modulo: ${eval(17 % 5)} # 2
|
|
708
1068
|
|
|
709
1069
|
# Comparisons (returns boolean)
|
|
710
1070
|
isGreater: ${eval(200 > 100)} # true
|
|
711
1071
|
isLess: ${eval(100 > 200)} # false
|
|
1072
|
+
isEqual: ${eval(10 == 10)} # true
|
|
712
1073
|
|
|
713
1074
|
# String comparisons
|
|
714
1075
|
isEqual: ${eval("hello" == "hello")} # true
|
|
715
1076
|
strictEqual: ${eval("foo" === "foo")} # true
|
|
1077
|
+
notEqual: ${eval("a" != "b")} # true
|
|
716
1078
|
|
|
717
1079
|
# Complex expressions
|
|
718
1080
|
complex: ${eval((10 + 5) * 2)} # 30
|
|
1081
|
+
percentage: ${eval((75 / 100) * 200)} # 150
|
|
1082
|
+
|
|
1083
|
+
# With variables
|
|
1084
|
+
threshold: 50
|
|
1085
|
+
value: 75
|
|
1086
|
+
aboveThreshold: ${eval(${value} > ${threshold})} # true
|
|
719
1087
|
```
|
|
720
1088
|
|
|
721
|
-
|
|
1089
|
+
**Supported operators:**
|
|
1090
|
+
|
|
1091
|
+
| Category | Operators |
|
|
1092
|
+
|----------|-----------|
|
|
1093
|
+
| Arithmetic | `+` `-` `*` `/` `%` |
|
|
1094
|
+
| Comparison | `==` `!=` `===` `!==` `>` `<` `>=` `<=` |
|
|
1095
|
+
| Logical | `&&` `\|\|` `!` |
|
|
1096
|
+
| Grouping | `( )` |
|
|
1097
|
+
|
|
1098
|
+
**Security:**
|
|
1099
|
+
- Does NOT use JavaScript's `eval()`
|
|
1100
|
+
- Uses safe expression parser (subscript)
|
|
1101
|
+
- No access to global scope or functions
|
|
1102
|
+
- Only mathematical and logical operations allowed
|
|
1103
|
+
|
|
1104
|
+
---
|
|
1105
|
+
|
|
1106
|
+
### If Expressions
|
|
722
1107
|
|
|
723
1108
|
Conditional expressions using ternary syntax. This is an alias for `eval` with a more intuitive name for conditionals.
|
|
724
1109
|
|
|
725
|
-
```
|
|
1110
|
+
```yaml
|
|
726
1111
|
# Basic ternary (condition ? "yes" : "no")
|
|
727
|
-
status: ${if(
|
|
1112
|
+
status: ${if(5 > 3 ? "yes" : "no")} # "yes"
|
|
728
1113
|
|
|
729
1114
|
# With variables
|
|
730
1115
|
threshold: 50
|
|
731
1116
|
value: 75
|
|
732
|
-
result: ${if(
|
|
1117
|
+
result: ${if(${value} > ${threshold} ? "above" : "below")} # "above"
|
|
733
1118
|
|
|
734
1119
|
# Nested ternary (if/else if/else)
|
|
735
1120
|
score: 85
|
|
736
|
-
grade: ${if(
|
|
1121
|
+
grade: ${if(${score} >= 90 ? "A" : ${score} >= 80 ? "B" : "C")} # "B"
|
|
737
1122
|
|
|
738
1123
|
# Boolean result (no ternary needed)
|
|
739
|
-
isValid: ${if(${
|
|
1124
|
+
isValid: ${if(${value} > 0)} # true
|
|
740
1125
|
|
|
741
1126
|
# Logical operators
|
|
742
1127
|
enabled: true
|
|
743
1128
|
count: 5
|
|
744
|
-
canProceed: ${if(${
|
|
1129
|
+
canProceed: ${if(${enabled} && ${count} > 0)} # true
|
|
1130
|
+
hasIssues: ${if(!${enabled} || ${count} == 0)} # false
|
|
745
1131
|
```
|
|
746
1132
|
|
|
747
1133
|
**Supported operators:**
|
|
@@ -755,7 +1141,7 @@ canProceed: ${if(${self:enabled} && ${self:count} > 0)} # true
|
|
|
755
1141
|
|
|
756
1142
|
**Serverless deployment examples:**
|
|
757
1143
|
|
|
758
|
-
```
|
|
1144
|
+
```yaml
|
|
759
1145
|
service: my-service
|
|
760
1146
|
|
|
761
1147
|
provider:
|
|
@@ -765,21 +1151,21 @@ provider:
|
|
|
765
1151
|
|
|
766
1152
|
custom:
|
|
767
1153
|
# Different memory by stage
|
|
768
|
-
memorySize:
|
|
1154
|
+
memorySize: ${if(${provider.stage} === "prod" ? 1024 : 512)}
|
|
769
1155
|
|
|
770
1156
|
# Different log retention by stage
|
|
771
|
-
logRetention: ${if(
|
|
1157
|
+
logRetention: ${if(${provider.stage} === "prod" ? 30 : 7)}
|
|
772
1158
|
|
|
773
1159
|
# Enable features per environment
|
|
774
|
-
enableDebugEndpoints: ${if(
|
|
775
|
-
enableMetrics: ${if(
|
|
1160
|
+
enableDebugEndpoints: ${if(${provider.stage} !== "prod")}
|
|
1161
|
+
enableMetrics: ${if(${provider.stage} === "prod")}
|
|
776
1162
|
|
|
777
1163
|
# Regional settings
|
|
778
|
-
replicaCount: ${if(
|
|
1164
|
+
replicaCount: ${if(${provider.region} === "us-east-1" ? 3 : 1)}
|
|
779
1165
|
|
|
780
1166
|
# Conditional IAM role (use predefined role in prod, inline in dev)
|
|
781
|
-
useExternalRole: ${if(
|
|
782
|
-
role: ${if(
|
|
1167
|
+
useExternalRole: ${if(${provider.stage} === "prod")}
|
|
1168
|
+
role: ${if(${custom.useExternalRole} ? "arn:aws:iam::123:role/prod-role" : null)}
|
|
783
1169
|
|
|
784
1170
|
functions:
|
|
785
1171
|
api:
|
|
@@ -797,29 +1183,62 @@ functions:
|
|
|
797
1183
|
enabled: ${custom.enableMetrics}
|
|
798
1184
|
```
|
|
799
1185
|
|
|
800
|
-
|
|
1186
|
+
---
|
|
1187
|
+
|
|
1188
|
+
### Filters (Experimental)
|
|
801
1189
|
|
|
802
1190
|
Pipe resolved values through transformation functions like case conversion.
|
|
803
1191
|
|
|
804
|
-
```
|
|
805
|
-
|
|
1192
|
+
```yaml
|
|
1193
|
+
# String transformations
|
|
1194
|
+
toUpperCaseString: ${'value' | toUpperCase } # 'VALUE'
|
|
1195
|
+
toLowerCaseString: ${'VALUE' | toLowerCase } # 'value'
|
|
806
1196
|
|
|
807
|
-
|
|
1197
|
+
# Case conversions
|
|
1198
|
+
toKebabCaseString: ${'valueHere' | toKebabCase } # 'value-here'
|
|
1199
|
+
toCamelCaseString: ${'value-here' | toCamelCase } # 'valueHere'
|
|
808
1200
|
|
|
1201
|
+
# Chaining filters
|
|
809
1202
|
key: lol_hi
|
|
1203
|
+
transformed: ${key | toKebabCase | toUpperCase } # 'LOL-HI'
|
|
1204
|
+
|
|
1205
|
+
# With variables
|
|
1206
|
+
serviceName: MyServiceName
|
|
1207
|
+
serviceSlug: ${serviceName | toKebabCase} # 'my-service-name'
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
**Built-in filters:**
|
|
1211
|
+
- `toUpperCase` - Convert to uppercase
|
|
1212
|
+
- `toLowerCase` - Convert to lowercase
|
|
1213
|
+
- `toKebabCase` - Convert to kebab-case
|
|
1214
|
+
- `toCamelCase` - Convert to camelCase
|
|
810
1215
|
|
|
811
|
-
|
|
1216
|
+
**Custom filters:**
|
|
812
1217
|
|
|
813
|
-
|
|
1218
|
+
```javascript
|
|
1219
|
+
const config = await configorama('config.yml', {
|
|
1220
|
+
filters: {
|
|
1221
|
+
// Custom filter
|
|
1222
|
+
reverse: (value) => value.split('').reverse().join(''),
|
|
1223
|
+
// Filter with options
|
|
1224
|
+
truncate: (value, length = 10) => value.substring(0, length)
|
|
1225
|
+
}
|
|
1226
|
+
})
|
|
1227
|
+
```
|
|
814
1228
|
|
|
815
|
-
|
|
1229
|
+
```yaml
|
|
1230
|
+
# Using custom filters
|
|
1231
|
+
reversed: ${'hello' | reverse} # 'olleh'
|
|
1232
|
+
truncated: ${'very long string' | truncate(5)} # 'very '
|
|
816
1233
|
```
|
|
817
1234
|
|
|
818
|
-
|
|
1235
|
+
---
|
|
1236
|
+
|
|
1237
|
+
### Functions (Experimental)
|
|
819
1238
|
|
|
820
1239
|
Apply built-in functions to combine, transform, or manipulate values.
|
|
821
1240
|
|
|
822
|
-
```
|
|
1241
|
+
```yaml
|
|
823
1242
|
object:
|
|
824
1243
|
one: once
|
|
825
1244
|
two: twice
|
|
@@ -828,165 +1247,324 @@ objectTwo:
|
|
|
828
1247
|
three: third
|
|
829
1248
|
four: fourth
|
|
830
1249
|
|
|
1250
|
+
# Merge objects
|
|
831
1251
|
mergeObjects: ${merge(${object}, ${objectTwo})}
|
|
832
|
-
|
|
1252
|
+
# Result: { one: 'once', two: 'twice', three: 'third', four: 'fourth' }
|
|
833
1253
|
|
|
834
|
-
|
|
1254
|
+
# String concatenation
|
|
1255
|
+
fullName: ${concat(${firstName}, ' ', ${lastName})}
|
|
835
1256
|
|
|
836
|
-
|
|
1257
|
+
# Array operations
|
|
1258
|
+
items:
|
|
1259
|
+
- a
|
|
1260
|
+
- b
|
|
1261
|
+
- c
|
|
837
1262
|
|
|
838
|
-
|
|
1263
|
+
joinedItems: ${join(${items}, ', ')} # 'a, b, c'
|
|
1264
|
+
```
|
|
839
1265
|
|
|
840
|
-
|
|
1266
|
+
**Built-in functions:**
|
|
1267
|
+
- `merge(obj1, obj2, ...)` - Merge multiple objects
|
|
1268
|
+
- `concat(str1, str2, ...)` - Concatenate strings
|
|
1269
|
+
- `join(array, separator)` - Join array elements
|
|
841
1270
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
1. Use the baked in javascript method for [sync](https://github.com/DavidWells/configorama/blob/master/tests/syncValues/syncValue.yml) or [aysnc](https://github.com/DavidWells/configorama/blob/master/tests/asyncValues/asyncValue.yml) resolution.
|
|
845
|
-
|
|
846
|
-
2. Add your own variable syntax and resolver.
|
|
847
|
-
|
|
848
|
-
```js
|
|
849
|
-
const config = configorama('path/to/configFile', {
|
|
850
|
-
variableSources: [{
|
|
851
|
-
// Variable type name (used in metadata)
|
|
852
|
-
type: 'consul',
|
|
853
|
-
// Source type for config wizard behavior (see table below)
|
|
854
|
-
source: 'remote',
|
|
855
|
-
// Prefix shown in syntax examples
|
|
856
|
-
prefix: 'consul',
|
|
857
|
-
// Example syntax for documentation
|
|
858
|
-
syntax: '${consul:path/to/key}',
|
|
859
|
-
// Description for help text
|
|
860
|
-
description: 'Resolves values from Consul KV store',
|
|
861
|
-
// Match variables ${consul:xyz}
|
|
862
|
-
match: RegExp(/^consul:/g),
|
|
863
|
-
// Custom variable source. Must return a promise
|
|
864
|
-
resolver: (varToProcess, opts, currentObject) => {
|
|
865
|
-
// Make remote call to consul
|
|
866
|
-
return Promise.resolve(varToProcess)
|
|
867
|
-
}
|
|
868
|
-
}]
|
|
869
|
-
})
|
|
870
|
-
console.log(config)
|
|
871
|
-
```
|
|
1271
|
+
**Custom functions:**
|
|
872
1272
|
|
|
873
|
-
|
|
1273
|
+
```javascript
|
|
1274
|
+
const config = await configorama('config.yml', {
|
|
1275
|
+
functions: {
|
|
1276
|
+
// Custom function
|
|
1277
|
+
add: (a, b) => a + b,
|
|
1278
|
+
// Function with multiple args
|
|
1279
|
+
between: (val, min, max) => val >= min && val <= max
|
|
1280
|
+
}
|
|
1281
|
+
})
|
|
1282
|
+
```
|
|
874
1283
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1284
|
+
```yaml
|
|
1285
|
+
# Using custom functions
|
|
1286
|
+
sum: ${add(5, 10)} # 15
|
|
1287
|
+
value: 75
|
|
1288
|
+
inRange: ${between(${value}, 50, 100)} # true
|
|
1289
|
+
```
|
|
878
1290
|
|
|
879
|
-
|
|
1291
|
+
---
|
|
880
1292
|
|
|
881
|
-
|
|
1293
|
+
## API Reference
|
|
882
1294
|
|
|
883
|
-
|
|
884
|
-
|--------|-------------|-----------------|----------|
|
|
885
|
-
| `'user'` | Values provided by user at runtime | Prompt user for value | `env`, `opt` |
|
|
886
|
-
| `'config'` | Values from config files or self-references | Check existence, can create | `self`, `file`, `text` |
|
|
887
|
-
| `'remote'` | Values from external services | Fetch, prompt if missing, can write back | `ssm`, `vault`, `consul` |
|
|
888
|
-
| `'readonly'` | Computed or system-derived values | Display only, cannot modify | `git`, `cron`, `eval` |
|
|
1295
|
+
### Async API
|
|
889
1296
|
|
|
890
|
-
|
|
1297
|
+
The primary async API for resolving configurations.
|
|
891
1298
|
|
|
892
|
-
|
|
893
|
-
|----------|-------------|-------------|
|
|
894
|
-
| `${env:VAR}` | `user` | Environment variables |
|
|
895
|
-
| `${opt:flag}` | `user` | CLI option flags |
|
|
896
|
-
| `${param:key}` | `user` | Parameter values |
|
|
897
|
-
| `${self:key}` | `config` | Self references |
|
|
898
|
-
| `${file(path)}` | `config` | File references |
|
|
899
|
-
| `${text(path)}` | `config` | Raw text file references |
|
|
900
|
-
| `${git:branch}` | `readonly` | Git repository data |
|
|
901
|
-
| `${cron(expr)}` | `readonly` | Cron expression conversion |
|
|
902
|
-
| `${eval(expr)}` | `readonly` | Math/logic evaluation |
|
|
903
|
-
| `${if(expr)}` | `readonly` | Conditional expressions |
|
|
1299
|
+
**Signature:**
|
|
904
1300
|
|
|
905
|
-
|
|
1301
|
+
```typescript
|
|
1302
|
+
function configorama<T = any>(
|
|
1303
|
+
configPathOrObject: string | object,
|
|
1304
|
+
settings?: ConfigoramaSettings
|
|
1305
|
+
): Promise<T | ConfigoramaResult<T>>
|
|
1306
|
+
```
|
|
906
1307
|
|
|
907
|
-
|
|
908
|
-
|--------|------|---------|-------------|
|
|
909
|
-
| `options` | object | `{}` | CLI options/flags to populate `${opt:xyz}` variables |
|
|
910
|
-
| `syntax` | string/RegExp | `${...}` | Custom variable syntax regex pattern |
|
|
911
|
-
| `allowUnknownVariableTypes` | boolean \| string[] | `false` | Allow unknown variable types to pass through (e.g., `${ssm:path}`) |
|
|
912
|
-
| `allowUnresolvedVariables` | boolean \| string[] | `false` | Allow known variable types that can't be resolved to pass through |
|
|
913
|
-
| `allowUndefinedValues` | boolean | `false` | Allow undefined to be an end result |
|
|
914
|
-
| `variableSources` | array | `[]` | Custom variable sources (see above) |
|
|
1308
|
+
**Parameters:**
|
|
915
1309
|
|
|
916
|
-
|
|
1310
|
+
| Parameter | Type | Required | Description |
|
|
1311
|
+
|-----------|------|----------|-------------|
|
|
1312
|
+
| `configPathOrObject` | `string \| object` | Yes | Path to config file or raw JavaScript object |
|
|
1313
|
+
| `settings` | `ConfigoramaSettings` | No | Configuration options |
|
|
917
1314
|
|
|
918
|
-
|
|
919
|
-
|
|
1315
|
+
**Settings object:**
|
|
1316
|
+
|
|
1317
|
+
```typescript
|
|
1318
|
+
interface ConfigoramaSettings {
|
|
1319
|
+
options?: Record<string, any> // CLI flags for ${opt:xyz}
|
|
1320
|
+
syntax?: string | RegExp // Custom variable syntax
|
|
1321
|
+
configDir?: string // Working directory for relative paths
|
|
1322
|
+
variableSources?: VariableSource[] // Custom variable resolvers
|
|
1323
|
+
filters?: Record<string, Function> // Custom filter functions
|
|
1324
|
+
functions?: Record<string, Function> // Custom functions
|
|
1325
|
+
allowUnknownVariableTypes?: boolean | string[] // Allow unknown var types
|
|
1326
|
+
allowUnresolvedVariables?: boolean | string[] // Allow unresolved vars
|
|
1327
|
+
allowUndefinedValues?: boolean // Allow undefined in output
|
|
1328
|
+
returnMetadata?: boolean // Return metadata with config
|
|
1329
|
+
mergeKeys?: string[] // Keys to merge in arrays
|
|
1330
|
+
filePathOverrides?: Record<string, string> // Override file paths
|
|
1331
|
+
}
|
|
1332
|
+
```
|
|
920
1333
|
|
|
921
|
-
**
|
|
1334
|
+
**Returns:**
|
|
922
1335
|
|
|
923
|
-
|
|
924
|
-
|
|
1336
|
+
- If `returnMetadata: false` (default): `Promise<T>` - Resolved config object
|
|
1337
|
+
- If `returnMetadata: true`: `Promise<ConfigoramaResult<T>>` - Object with config and metadata
|
|
1338
|
+
|
|
1339
|
+
**Example:**
|
|
925
1340
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
{ allowUnknownVariables: true } → { allowUnknownVariableTypes: true }
|
|
1341
|
+
```javascript
|
|
1342
|
+
const configorama = require('configorama')
|
|
929
1343
|
|
|
930
|
-
//
|
|
931
|
-
|
|
1344
|
+
// Basic usage
|
|
1345
|
+
const config = await configorama('./config.yml')
|
|
932
1346
|
|
|
933
|
-
//
|
|
934
|
-
|
|
1347
|
+
// With options
|
|
1348
|
+
const config = await configorama('./config.yml', {
|
|
1349
|
+
options: { stage: 'prod', region: 'us-east-1' },
|
|
1350
|
+
allowUnknownVariableTypes: ['ssm', 'cf']
|
|
1351
|
+
})
|
|
935
1352
|
|
|
936
|
-
//
|
|
937
|
-
|
|
938
|
-
|
|
1353
|
+
// With metadata
|
|
1354
|
+
const result = await configorama('./config.yml', {
|
|
1355
|
+
returnMetadata: true,
|
|
1356
|
+
options: { stage: 'prod' }
|
|
1357
|
+
})
|
|
939
1358
|
|
|
940
|
-
//
|
|
941
|
-
|
|
1359
|
+
console.log(result.config) // Resolved config
|
|
1360
|
+
console.log(result.metadata) // Variable metadata
|
|
1361
|
+
console.log(result.resolutionHistory) // Resolution steps
|
|
942
1362
|
```
|
|
943
1363
|
|
|
944
|
-
|
|
1364
|
+
---
|
|
1365
|
+
|
|
1366
|
+
### Sync API
|
|
1367
|
+
|
|
1368
|
+
Synchronous API for blocking config resolution.
|
|
945
1369
|
|
|
946
|
-
|
|
947
|
-
// Only allow specific unknown types
|
|
948
|
-
{ allowUnknownVariableTypes: ['ssm', 'cf', 's3'] }
|
|
1370
|
+
**Signature:**
|
|
949
1371
|
|
|
950
|
-
|
|
951
|
-
|
|
1372
|
+
```typescript
|
|
1373
|
+
function configorama.sync<T = any>(
|
|
1374
|
+
configPathOrObject: string | object,
|
|
1375
|
+
settings?: ConfigoramaSettings
|
|
1376
|
+
): T
|
|
952
1377
|
```
|
|
953
1378
|
|
|
954
|
-
|
|
1379
|
+
**Parameters:**
|
|
955
1380
|
|
|
956
|
-
|
|
1381
|
+
Same as async API, but `dynamicArgs` cannot be a function (must be serializable).
|
|
957
1382
|
|
|
958
|
-
|
|
1383
|
+
**Returns:**
|
|
959
1384
|
|
|
960
|
-
|
|
1385
|
+
`T` - Resolved config object (synchronously)
|
|
1386
|
+
|
|
1387
|
+
**Limitations:**
|
|
1388
|
+
|
|
1389
|
+
- Cannot use async functions in JavaScript/TypeScript file references
|
|
1390
|
+
- `dynamicArgs` must be serializable (not a function)
|
|
1391
|
+
- CLI args automatically parsed from `process.argv` if `options` not provided
|
|
1392
|
+
|
|
1393
|
+
**Example:**
|
|
961
1394
|
|
|
962
|
-
```
|
|
1395
|
+
```javascript
|
|
963
1396
|
const configorama = require('configorama')
|
|
964
|
-
const { buildVariableSyntax } = require('configorama')
|
|
965
1397
|
|
|
966
|
-
//
|
|
967
|
-
const config =
|
|
968
|
-
|
|
1398
|
+
// Basic sync usage
|
|
1399
|
+
const config = configorama.sync('./config.yml')
|
|
1400
|
+
|
|
1401
|
+
// With options
|
|
1402
|
+
const config = configorama.sync('./config.yml', {
|
|
969
1403
|
options: { stage: 'dev' }
|
|
970
1404
|
})
|
|
971
|
-
|
|
972
|
-
// Other examples:
|
|
973
|
-
buildVariableSyntax('${{', '}}') // ${{env:FOO}}
|
|
974
|
-
buildVariableSyntax('#{', '}') // #{env:FOO}
|
|
975
|
-
buildVariableSyntax('[[', ']]') // [[env:FOO]]
|
|
976
|
-
buildVariableSyntax('<', '>') // <env:FOO>
|
|
977
1405
|
```
|
|
978
1406
|
|
|
979
|
-
|
|
980
|
-
- Automatically excludes suffix characters from the allowed character class (prevents parsing issues)
|
|
981
|
-
- Supports nested variables by excluding `$` and `{` from values
|
|
982
|
-
- Third parameter `excludePatterns` is an array of strings to exclude via negative lookahead (default: `['AWS', 'stageVariables']`)
|
|
1407
|
+
---
|
|
983
1408
|
|
|
984
|
-
###
|
|
1409
|
+
### Analyze API
|
|
985
1410
|
|
|
986
|
-
|
|
1411
|
+
Analyze config structure without resolving variables.
|
|
987
1412
|
|
|
988
|
-
|
|
989
|
-
|
|
1413
|
+
**Signature:**
|
|
1414
|
+
|
|
1415
|
+
```typescript
|
|
1416
|
+
function configorama.analyze(
|
|
1417
|
+
configPathOrObject: string | object,
|
|
1418
|
+
settings?: ConfigoramaSettings
|
|
1419
|
+
): Promise<AnalyzeResult>
|
|
1420
|
+
```
|
|
1421
|
+
|
|
1422
|
+
**Returns:**
|
|
1423
|
+
|
|
1424
|
+
```typescript
|
|
1425
|
+
interface AnalyzeResult {
|
|
1426
|
+
originalConfig: object // Raw config object
|
|
1427
|
+
variables: Variable[] // All variables found
|
|
1428
|
+
uniqueVariables: Record<string, Variable[]> // Variables grouped by name
|
|
1429
|
+
fileDependencies: string[] // File references
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
interface Variable {
|
|
1433
|
+
variable: string // Full variable syntax (e.g., '${env:KEY}')
|
|
1434
|
+
variableType: string // Type (e.g., 'env', 'opt', 'file')
|
|
1435
|
+
variableName: string // Name/path (e.g., 'KEY')
|
|
1436
|
+
variablePath: string // Location in config (e.g., 'database.host')
|
|
1437
|
+
defaultValue: any // Default value if provided
|
|
1438
|
+
hasDefault: boolean // Whether default exists
|
|
1439
|
+
}
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
**Example:**
|
|
1443
|
+
|
|
1444
|
+
```javascript
|
|
1445
|
+
const configorama = require('configorama')
|
|
1446
|
+
|
|
1447
|
+
const analysis = await configorama.analyze('./config.yml')
|
|
1448
|
+
|
|
1449
|
+
console.log(`Found ${analysis.variables.length} variables`)
|
|
1450
|
+
console.log(`File dependencies:`, analysis.fileDependencies)
|
|
1451
|
+
|
|
1452
|
+
// List all environment variables required
|
|
1453
|
+
const envVars = analysis.variables
|
|
1454
|
+
.filter(v => v.variableType === 'env' && !v.hasDefault)
|
|
1455
|
+
.map(v => v.variableName)
|
|
1456
|
+
|
|
1457
|
+
console.log('Required env vars:', envVars)
|
|
1458
|
+
```
|
|
1459
|
+
|
|
1460
|
+
**Use cases:**
|
|
1461
|
+
- Generate documentation of required environment variables
|
|
1462
|
+
- Validate config structure in CI/CD
|
|
1463
|
+
- Build dependency graphs
|
|
1464
|
+
- Audit external dependencies before resolution
|
|
1465
|
+
|
|
1466
|
+
---
|
|
1467
|
+
|
|
1468
|
+
### Format Utilities
|
|
1469
|
+
|
|
1470
|
+
Parse various config formats to JavaScript objects.
|
|
1471
|
+
|
|
1472
|
+
**Available parsers:**
|
|
1473
|
+
|
|
1474
|
+
```javascript
|
|
1475
|
+
const { format } = require('configorama')
|
|
1476
|
+
|
|
1477
|
+
// Parse YAML
|
|
1478
|
+
const yamlObj = format.yaml.parse('key: value')
|
|
1479
|
+
|
|
1480
|
+
// Parse JSON5
|
|
1481
|
+
const jsonObj = format.json5.parse('{ key: "value", }')
|
|
1482
|
+
|
|
1483
|
+
// Parse TOML
|
|
1484
|
+
const tomlObj = format.toml.parse('key = "value"')
|
|
1485
|
+
|
|
1486
|
+
// Parse INI
|
|
1487
|
+
const iniObj = format.ini.parse('[section]\nkey=value')
|
|
1488
|
+
|
|
1489
|
+
// Parse HCL (requires @cdktf/hcl2json)
|
|
1490
|
+
const hclObj = await format.hcl.parse('variable "example" { default = "value" }')
|
|
1491
|
+
```
|
|
1492
|
+
|
|
1493
|
+
**Parser methods:**
|
|
1494
|
+
|
|
1495
|
+
Each parser has:
|
|
1496
|
+
- `parse(content)` - Parse string to JavaScript object
|
|
1497
|
+
- `stringify(obj)` - Convert JavaScript object to format string (if supported)
|
|
1498
|
+
|
|
1499
|
+
---
|
|
1500
|
+
|
|
1501
|
+
## Configuration Options
|
|
1502
|
+
|
|
1503
|
+
### Custom Variable Syntax
|
|
1504
|
+
|
|
1505
|
+
Use the `syntax` option to change the variable delimiters. You can provide a regex string directly or use `buildVariableSyntax()` to generate one with proper character escaping:
|
|
1506
|
+
|
|
1507
|
+
```javascript
|
|
1508
|
+
const configorama = require('configorama')
|
|
1509
|
+
const { buildVariableSyntax } = require('configorama')
|
|
1510
|
+
|
|
1511
|
+
// Using buildVariableSyntax helper (recommended)
|
|
1512
|
+
const config = await configorama(configFile, {
|
|
1513
|
+
syntax: buildVariableSyntax('{{', '}}'), // Mustache-style: {{env:FOO}}
|
|
1514
|
+
options: { stage: 'dev' }
|
|
1515
|
+
})
|
|
1516
|
+
|
|
1517
|
+
// Other examples:
|
|
1518
|
+
buildVariableSyntax('${{', '}}') // ${{env:FOO}}
|
|
1519
|
+
buildVariableSyntax('#{', '}') // #{env:FOO}
|
|
1520
|
+
buildVariableSyntax('[[', ']]') // [[env:FOO]]
|
|
1521
|
+
buildVariableSyntax('<', '>') // <env:FOO>
|
|
1522
|
+
```
|
|
1523
|
+
|
|
1524
|
+
**Function signature:**
|
|
1525
|
+
|
|
1526
|
+
```typescript
|
|
1527
|
+
function buildVariableSyntax(
|
|
1528
|
+
prefix: string = '${',
|
|
1529
|
+
suffix: string = '}',
|
|
1530
|
+
excludePatterns: string[] = ['AWS', 'stageVariables']
|
|
1531
|
+
): string
|
|
1532
|
+
```
|
|
1533
|
+
|
|
1534
|
+
The `buildVariableSyntax()` function:
|
|
1535
|
+
- Automatically excludes suffix characters from the allowed character class (prevents parsing issues)
|
|
1536
|
+
- Supports nested variables by excluding `$` and `{` from values
|
|
1537
|
+
- Third parameter `excludePatterns` is an array of strings to exclude via negative lookahead
|
|
1538
|
+
|
|
1539
|
+
**Example with custom syntax:**
|
|
1540
|
+
|
|
1541
|
+
```javascript
|
|
1542
|
+
const config = await configorama('config.yml', {
|
|
1543
|
+
syntax: buildVariableSyntax('{{', '}}')
|
|
1544
|
+
})
|
|
1545
|
+
```
|
|
1546
|
+
|
|
1547
|
+
```yaml
|
|
1548
|
+
# config.yml with {{ }} syntax
|
|
1549
|
+
apiKey: {{env:API_KEY}}
|
|
1550
|
+
stage: {{opt:stage, 'dev'}}
|
|
1551
|
+
database: {{file(./db.yml)}}
|
|
1552
|
+
```
|
|
1553
|
+
|
|
1554
|
+
---
|
|
1555
|
+
|
|
1556
|
+
### allowUnknownVariableTypes
|
|
1557
|
+
|
|
1558
|
+
Controls what happens when encountering unregistered variable types (e.g., `${ssm:path}` when `ssm` isn't a registered resolver).
|
|
1559
|
+
|
|
1560
|
+
**Type:** `boolean | string[]`
|
|
1561
|
+
|
|
1562
|
+
**Default:** `false`
|
|
1563
|
+
|
|
1564
|
+
**Behavior:**
|
|
1565
|
+
|
|
1566
|
+
```javascript
|
|
1567
|
+
// Allow ALL unknown types to pass through
|
|
990
1568
|
const config = await configorama(configFile, {
|
|
991
1569
|
allowUnknownVariableTypes: true,
|
|
992
1570
|
options: { stage: 'dev' }
|
|
@@ -1003,11 +1581,24 @@ const config = await configorama(configFile, {
|
|
|
1003
1581
|
// ${custom:thing} throws an error
|
|
1004
1582
|
```
|
|
1005
1583
|
|
|
1584
|
+
**Use cases:**
|
|
1585
|
+
- Multi-stage resolution (local resolution, then cloud provider resolves remaining vars)
|
|
1586
|
+
- Serverless Framework integration (let framework resolve SSM, CloudFormation refs)
|
|
1587
|
+
- Gradual migration (allow unknown types during transition period)
|
|
1588
|
+
|
|
1589
|
+
---
|
|
1590
|
+
|
|
1006
1591
|
### allowUnresolvedVariables
|
|
1007
1592
|
|
|
1008
1593
|
Controls what happens when a known resolver can't find a value (missing env vars, missing files, etc.).
|
|
1009
1594
|
|
|
1010
|
-
|
|
1595
|
+
**Type:** `boolean | string[]`
|
|
1596
|
+
|
|
1597
|
+
**Default:** `false`
|
|
1598
|
+
|
|
1599
|
+
**Behavior:**
|
|
1600
|
+
|
|
1601
|
+
```javascript
|
|
1011
1602
|
// Allow ALL unresolved variables to pass through
|
|
1012
1603
|
const config = await configorama(configFile, {
|
|
1013
1604
|
allowUnresolvedVariables: true,
|
|
@@ -1024,56 +1615,461 @@ const config = await configorama(configFile, {
|
|
|
1024
1615
|
// Input: { paramKey: '${param:x}', fileKey: '${file(missing.yml)}' }
|
|
1025
1616
|
// Output: { paramKey: '${param:x}', fileKey: '${file(missing.yml)}' }
|
|
1026
1617
|
|
|
1027
|
-
//
|
|
1618
|
+
// Mixed scenario
|
|
1028
1619
|
const config = await configorama(configFile, {
|
|
1029
|
-
allowUnresolvedVariables: ['param', 'file'],
|
|
1620
|
+
allowUnresolvedVariables: ['param', 'file'],
|
|
1030
1621
|
options: { stage: 'prod' }
|
|
1031
1622
|
})
|
|
1032
|
-
// Input: {
|
|
1033
|
-
//
|
|
1034
|
-
//
|
|
1623
|
+
// Input: {
|
|
1624
|
+
// key: '${env:MISSING_VAR}',
|
|
1625
|
+
// paramKey: '${param:x}',
|
|
1626
|
+
// fileKey: '${file(missing.yml)}'
|
|
1627
|
+
// }
|
|
1628
|
+
// Output: Error thrown because ${env:MISSING_VAR} cannot resolve
|
|
1629
|
+
// (param and file pass through, but env vars must resolve)
|
|
1035
1630
|
```
|
|
1036
1631
|
|
|
1037
|
-
|
|
1632
|
+
**Important notes:**
|
|
1633
|
+
- This option does NOT apply to `self:` or dotProp variables (e.g., `${foo.bar.baz}`)
|
|
1634
|
+
- Self-references are local config errors, not external dependencies
|
|
1635
|
+
- Useful for multi-stage resolution pipelines
|
|
1038
1636
|
|
|
1039
|
-
|
|
1637
|
+
**Use cases:**
|
|
1638
|
+
- Serverless Dashboard resolves params after local resolution
|
|
1639
|
+
- Gradual migration with optional external dependencies
|
|
1640
|
+
- Development mode where some services are unavailable
|
|
1040
1641
|
|
|
1041
|
-
|
|
1642
|
+
---
|
|
1042
1643
|
|
|
1043
|
-
|
|
1644
|
+
### Complete Options Reference
|
|
1044
1645
|
|
|
1045
|
-
|
|
1646
|
+
| Option | Type | Default | Description |
|
|
1647
|
+
|--------|------|---------|-------------|
|
|
1648
|
+
| `options` | `object` | `{}` | CLI options/flags to populate `${opt:xyz}` variables |
|
|
1649
|
+
| `syntax` | `string \| RegExp` | `${...}` | Custom variable syntax regex pattern |
|
|
1650
|
+
| `configDir` | `string` | directory of config file | Working directory for relative file paths |
|
|
1651
|
+
| `variableSources` | `VariableSource[]` | `[]` | Custom variable sources (see below) |
|
|
1652
|
+
| `filters` | `Record<string, Function>` | `{}` | Custom filter functions for pipe operator |
|
|
1653
|
+
| `functions` | `Record<string, Function>` | `{}` | Custom functions for `${fn(...)}` syntax |
|
|
1654
|
+
| `allowUnknownVariableTypes` | `boolean \| string[]` | `false` | Allow unknown variable types to pass through |
|
|
1655
|
+
| `allowUnresolvedVariables` | `boolean \| string[]` | `false` | Allow known types that can't resolve to pass through |
|
|
1656
|
+
| `allowUndefinedValues` | `boolean` | `false` | Allow undefined as a valid end result |
|
|
1657
|
+
| `returnMetadata` | `boolean` | `false` | Return both config and metadata about variables |
|
|
1658
|
+
| `mergeKeys` | `string[]` | `[]` | Keys to merge in arrays of objects |
|
|
1659
|
+
| `filePathOverrides` | `Record<string, string>` | `{}` | Map of file paths to override (for testing/mocking) |
|
|
1660
|
+
|
|
1661
|
+
**Legacy options (deprecated):**
|
|
1662
|
+
|
|
1663
|
+
| Legacy Option | New Equivalent |
|
|
1664
|
+
|---------------|----------------|
|
|
1665
|
+
| `allowUnknownVars` | `allowUnknownVariableTypes` |
|
|
1666
|
+
| `allowUnknownVariables` | `allowUnknownVariableTypes` |
|
|
1667
|
+
| `allowUnknownParams` | `allowUnresolvedVariables: ['param']` |
|
|
1668
|
+
| `allowUnknownFileRefs` | `allowUnresolvedVariables: ['file']` |
|
|
1669
|
+
|
|
1670
|
+
---
|
|
1046
1671
|
|
|
1047
|
-
|
|
1048
|
-
# Direct cycle - throws error
|
|
1049
|
-
a: ${self:b}
|
|
1050
|
-
b: ${self:a}
|
|
1051
|
-
# Error: Circular variable dependency detected: b → a → b
|
|
1672
|
+
## Custom Variable Sources
|
|
1052
1673
|
|
|
1053
|
-
|
|
1054
|
-
a: ${self:b}
|
|
1055
|
-
b: ${self:c}
|
|
1056
|
-
c: ${self:a}
|
|
1057
|
-
# Error: Circular variable dependency detected: c → a → b → c
|
|
1674
|
+
Configorama allows you to bring your own variable sources.
|
|
1058
1675
|
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1676
|
+
### Variable Source Types
|
|
1677
|
+
|
|
1678
|
+
The `source` property defines how the config wizard handles each variable type:
|
|
1679
|
+
|
|
1680
|
+
| Source | Description | Wizard Behavior | Examples |
|
|
1681
|
+
|--------|-------------|-----------------|----------|
|
|
1682
|
+
| `'user'` | Values provided by user at runtime | Prompt user for value | `env`, `opt` |
|
|
1683
|
+
| `'config'` | Values from config files or self-references | Check existence, can create | `self`, `file`, `text` |
|
|
1684
|
+
| `'remote'` | Values from external services | Fetch, prompt if missing, can write back | `ssm`, `vault`, `consul` |
|
|
1685
|
+
| `'readonly'` | Computed or system-derived values | Display only, cannot modify | `git`, `cron`, `eval` |
|
|
1686
|
+
|
|
1687
|
+
**Built-in variable sources and their types:**
|
|
1688
|
+
|
|
1689
|
+
| Variable | Source Type | Description |
|
|
1690
|
+
|----------|-------------|-------------|
|
|
1691
|
+
| `${env:VAR}` | `user` | Environment variables |
|
|
1692
|
+
| `${opt:flag}` | `user` | CLI option flags |
|
|
1693
|
+
| `${param:key}` | `user` | Parameter values |
|
|
1694
|
+
| `${self:key}` | `config` | Self references |
|
|
1695
|
+
| `${file(path)}` | `config` | File references |
|
|
1696
|
+
| `${text(path)}` | `config` | Raw text file references |
|
|
1697
|
+
| `${git:branch}` | `readonly` | Git repository data |
|
|
1698
|
+
| `${cron(expr)}` | `readonly` | Cron expression conversion |
|
|
1699
|
+
| `${eval(expr)}` | `readonly` | Math/logic evaluation |
|
|
1700
|
+
| `${if(expr)}` | `readonly` | Conditional expressions |
|
|
1701
|
+
|
|
1702
|
+
### Creating a Custom Resolver
|
|
1703
|
+
|
|
1704
|
+
There are 2 ways to resolve variables from custom sources:
|
|
1705
|
+
|
|
1706
|
+
1. **Use built-in JavaScript method** for [sync](https://github.com/DavidWells/configorama/blob/master/tests/syncValues/syncValue.yml) or [async](https://github.com/DavidWells/configorama/blob/master/tests/asyncValues/asyncValue.yml) resolution.
|
|
1707
|
+
|
|
1708
|
+
2. **Add your own variable syntax and resolver:**
|
|
1709
|
+
|
|
1710
|
+
```javascript
|
|
1711
|
+
const configorama = require('configorama')
|
|
1712
|
+
|
|
1713
|
+
const config = await configorama('path/to/configFile', {
|
|
1714
|
+
variableSources: [{
|
|
1715
|
+
// Variable type name (used in metadata)
|
|
1716
|
+
type: 'consul',
|
|
1717
|
+
|
|
1718
|
+
// Source type for config wizard behavior
|
|
1719
|
+
source: 'remote',
|
|
1720
|
+
|
|
1721
|
+
// Prefix shown in syntax examples
|
|
1722
|
+
prefix: 'consul',
|
|
1723
|
+
|
|
1724
|
+
// Example syntax for documentation
|
|
1725
|
+
syntax: '${consul:path/to/key}',
|
|
1726
|
+
|
|
1727
|
+
// Description for help text
|
|
1728
|
+
description: 'Resolves values from Consul KV store',
|
|
1729
|
+
|
|
1730
|
+
// Match variables ${consul:xyz}
|
|
1731
|
+
match: RegExp(/^consul:/g),
|
|
1732
|
+
|
|
1733
|
+
// Custom variable source. Must return a promise
|
|
1734
|
+
resolver: async (varToProcess, opts, currentObject) => {
|
|
1735
|
+
// varToProcess = 'consul:path/to/key'
|
|
1736
|
+
const consulPath = varToProcess.replace(/^consul:/, '')
|
|
1737
|
+
|
|
1738
|
+
// Make remote call to consul
|
|
1739
|
+
const consulClient = require('consul')()
|
|
1740
|
+
const result = await consulClient.kv.get(consulPath)
|
|
1741
|
+
|
|
1742
|
+
return result.Value
|
|
1743
|
+
}
|
|
1744
|
+
}]
|
|
1745
|
+
})
|
|
1746
|
+
|
|
1747
|
+
console.log(config)
|
|
1065
1748
|
```
|
|
1066
1749
|
|
|
1067
|
-
**
|
|
1750
|
+
**This would match:**
|
|
1751
|
+
|
|
1752
|
+
```yaml
|
|
1753
|
+
key: ${consul:path/to/my/key}
|
|
1754
|
+
```
|
|
1755
|
+
|
|
1756
|
+
**Variable source interface:**
|
|
1757
|
+
|
|
1758
|
+
```typescript
|
|
1759
|
+
interface VariableSource {
|
|
1760
|
+
type: string // Type name (e.g., 'consul', 'ssm')
|
|
1761
|
+
source: 'user' | 'config' | 'remote' | 'readonly'
|
|
1762
|
+
prefix?: string // Prefix for examples (defaults to type)
|
|
1763
|
+
syntax: string // Example syntax (e.g., '${consul:key}')
|
|
1764
|
+
description?: string // Help text description
|
|
1765
|
+
match: RegExp // Regex to match variables
|
|
1766
|
+
resolver: ( // Resolution function
|
|
1767
|
+
variable: string, // Variable string (e.g., 'consul:key')
|
|
1768
|
+
options: object, // Options from configorama call
|
|
1769
|
+
currentConfig: object // Current partially-resolved config
|
|
1770
|
+
) => Promise<any>
|
|
1771
|
+
collectMetadata?: () => any // Optional: collect custom metadata
|
|
1772
|
+
metadataKey?: string // Optional: key for custom metadata
|
|
1773
|
+
}
|
|
1774
|
+
```
|
|
1775
|
+
|
|
1776
|
+
**Advanced example with AWS SSM:**
|
|
1777
|
+
|
|
1778
|
+
```javascript
|
|
1779
|
+
const AWS = require('aws-sdk')
|
|
1780
|
+
const ssm = new AWS.SSM()
|
|
1781
|
+
|
|
1782
|
+
const config = await configorama('config.yml', {
|
|
1783
|
+
variableSources: [{
|
|
1784
|
+
type: 'ssm',
|
|
1785
|
+
source: 'remote',
|
|
1786
|
+
syntax: '${ssm:/path/to/parameter}',
|
|
1787
|
+
description: 'Resolves values from AWS Systems Manager Parameter Store',
|
|
1788
|
+
match: /^ssm:/,
|
|
1789
|
+
resolver: async (variable, options, currentConfig) => {
|
|
1790
|
+
const paramPath = variable.replace(/^ssm:/, '')
|
|
1791
|
+
|
|
1792
|
+
try {
|
|
1793
|
+
const result = await ssm.getParameter({
|
|
1794
|
+
Name: paramPath,
|
|
1795
|
+
WithDecryption: true
|
|
1796
|
+
}).promise()
|
|
1797
|
+
|
|
1798
|
+
return result.Parameter.Value
|
|
1799
|
+
} catch (err) {
|
|
1800
|
+
if (options.allowUnresolvedVariables) {
|
|
1801
|
+
return `\${${variable}}` // Pass through unresolved
|
|
1802
|
+
}
|
|
1803
|
+
throw new Error(`SSM parameter not found: ${paramPath}`)
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
}]
|
|
1807
|
+
})
|
|
1808
|
+
```
|
|
1809
|
+
|
|
1810
|
+
```yaml
|
|
1811
|
+
# config.yml
|
|
1812
|
+
database:
|
|
1813
|
+
password: ${ssm:/myapp/prod/db-password}
|
|
1814
|
+
apiKey: ${ssm:/myapp/prod/api-key}
|
|
1815
|
+
```
|
|
1816
|
+
|
|
1817
|
+
---
|
|
1818
|
+
|
|
1819
|
+
## CLI Usage
|
|
1820
|
+
|
|
1821
|
+
Configorama includes a CLI tool for resolving configs from the command line.
|
|
1822
|
+
|
|
1823
|
+
### Basic Commands
|
|
1824
|
+
|
|
1825
|
+
```bash
|
|
1826
|
+
# Resolve a config file
|
|
1827
|
+
configorama config.yml
|
|
1828
|
+
|
|
1829
|
+
# Resolve and write to output file
|
|
1830
|
+
configorama config.yml --output resolved.json
|
|
1831
|
+
|
|
1832
|
+
# Resolve with CLI options
|
|
1833
|
+
configorama config.yml --stage prod --region us-east-1
|
|
1834
|
+
|
|
1835
|
+
# Show info about variables
|
|
1836
|
+
configorama config.yml --info
|
|
1837
|
+
|
|
1838
|
+
# Verify config (check for errors without resolving)
|
|
1839
|
+
configorama config.yml --verify
|
|
1840
|
+
|
|
1841
|
+
# Extract specific path from config
|
|
1842
|
+
configorama config.yml database.host
|
|
1843
|
+
|
|
1844
|
+
# Output as YAML
|
|
1845
|
+
configorama config.yml --format yaml
|
|
1846
|
+
```
|
|
1847
|
+
|
|
1848
|
+
### Command Options
|
|
1849
|
+
|
|
1850
|
+
```text
|
|
1851
|
+
Usage:
|
|
1852
|
+
configorama [options] <file> [path]
|
|
1853
|
+
|
|
1854
|
+
Options:
|
|
1855
|
+
-h, --help Show this help message
|
|
1856
|
+
-v, --version Show version number
|
|
1857
|
+
-o, --output <file> Write output to file instead of stdout
|
|
1858
|
+
-f, --format <format> Output format: json, yaml, or js (default: json)
|
|
1859
|
+
-d, --debug Enable debug mode
|
|
1860
|
+
-i, --info Show info about the config
|
|
1861
|
+
-V, --verify Verify the config
|
|
1862
|
+
--param <key=value> Pass parameter values (can be used multiple times)
|
|
1863
|
+
--allow-unknown Allow unknown variables to pass through
|
|
1864
|
+
--allow-undefined Allow undefined values in the final output
|
|
1865
|
+
|
|
1866
|
+
Path Extraction:
|
|
1867
|
+
configorama config.yml database.host Extract specific value
|
|
1868
|
+
configorama config.yml functions[0] Extract from array
|
|
1869
|
+
```
|
|
1870
|
+
|
|
1871
|
+
### CLI Examples
|
|
1872
|
+
|
|
1873
|
+
**Basic resolution:**
|
|
1874
|
+
|
|
1875
|
+
```bash
|
|
1876
|
+
# Input: config.yml
|
|
1877
|
+
apiKey: ${env:API_KEY}
|
|
1878
|
+
stage: ${opt:stage, 'dev'}
|
|
1879
|
+
|
|
1880
|
+
# Command
|
|
1881
|
+
export API_KEY=secret123
|
|
1882
|
+
configorama config.yml --stage prod
|
|
1883
|
+
|
|
1884
|
+
# Output
|
|
1885
|
+
{
|
|
1886
|
+
"apiKey": "secret123",
|
|
1887
|
+
"stage": "prod"
|
|
1888
|
+
}
|
|
1889
|
+
```
|
|
1890
|
+
|
|
1891
|
+
**With parameters:**
|
|
1892
|
+
|
|
1893
|
+
```bash
|
|
1894
|
+
configorama config.yml \
|
|
1895
|
+
--stage prod \
|
|
1896
|
+
--param "domain=myapp.com" \
|
|
1897
|
+
--param "apiKey=secret123"
|
|
1898
|
+
```
|
|
1899
|
+
|
|
1900
|
+
**Extract specific path:**
|
|
1068
1901
|
|
|
1069
|
-
|
|
1902
|
+
```bash
|
|
1903
|
+
# config.yml
|
|
1904
|
+
database:
|
|
1905
|
+
host: localhost
|
|
1906
|
+
port: 5432
|
|
1907
|
+
|
|
1908
|
+
# Extract database.host
|
|
1909
|
+
configorama config.yml database.host
|
|
1910
|
+
# Output: localhost
|
|
1911
|
+
|
|
1912
|
+
# Extract database config as JSON
|
|
1913
|
+
configorama config.yml database --format json
|
|
1914
|
+
# Output: {"host":"localhost","port":5432}
|
|
1915
|
+
```
|
|
1916
|
+
|
|
1917
|
+
**Output to file:**
|
|
1918
|
+
|
|
1919
|
+
```bash
|
|
1920
|
+
configorama config.yml --output resolved.json
|
|
1921
|
+
configorama config.yml --output resolved.yml --format yaml
|
|
1922
|
+
```
|
|
1923
|
+
|
|
1924
|
+
**Show variable info:**
|
|
1925
|
+
|
|
1926
|
+
```bash
|
|
1927
|
+
configorama config.yml --info
|
|
1928
|
+
|
|
1929
|
+
# Output:
|
|
1930
|
+
# Found 15 variables
|
|
1931
|
+
# env: 5
|
|
1932
|
+
# opt: 3
|
|
1933
|
+
# self: 4
|
|
1934
|
+
# file: 2
|
|
1935
|
+
# git: 1
|
|
1936
|
+
#
|
|
1937
|
+
# Required environment variables:
|
|
1938
|
+
# - API_KEY
|
|
1939
|
+
# - DB_HOST
|
|
1940
|
+
# - DB_PASSWORD
|
|
1941
|
+
#
|
|
1942
|
+
# File dependencies:
|
|
1943
|
+
# - ./secrets.yml
|
|
1944
|
+
# - ./config/database.yml
|
|
1945
|
+
```
|
|
1946
|
+
|
|
1947
|
+
**Verify without resolving:**
|
|
1948
|
+
|
|
1949
|
+
```bash
|
|
1950
|
+
configorama config.yml --verify
|
|
1951
|
+
|
|
1952
|
+
# Output:
|
|
1953
|
+
# ✓ Config structure valid
|
|
1954
|
+
# ✓ No circular dependencies
|
|
1955
|
+
# ✓ All file references exist
|
|
1956
|
+
# ! Warning: 3 environment variables not set
|
|
1957
|
+
# - API_KEY
|
|
1958
|
+
# - DB_HOST
|
|
1959
|
+
# - DB_PASSWORD
|
|
1960
|
+
```
|
|
1961
|
+
|
|
1962
|
+
---
|
|
1963
|
+
|
|
1964
|
+
## Testing
|
|
1070
1965
|
|
|
1071
|
-
|
|
1966
|
+
### Running Tests
|
|
1072
1967
|
|
|
1073
|
-
|
|
1968
|
+
```bash
|
|
1969
|
+
# Run all tests
|
|
1970
|
+
npm test
|
|
1971
|
+
|
|
1972
|
+
# Run only fast tests (excludes slow tests)
|
|
1973
|
+
npm run test:lib
|
|
1974
|
+
|
|
1975
|
+
# Run API tests
|
|
1976
|
+
npm run test:api
|
|
1977
|
+
|
|
1978
|
+
# Run tests in a specific directory
|
|
1979
|
+
npm run test:tests
|
|
1980
|
+
|
|
1981
|
+
# Run slow tests only
|
|
1982
|
+
npm run test:slow
|
|
1983
|
+
|
|
1984
|
+
# Watch mode (reruns on file changes)
|
|
1985
|
+
npm run watch
|
|
1986
|
+
|
|
1987
|
+
# Type checking
|
|
1988
|
+
npm run typecheck
|
|
1989
|
+
```
|
|
1990
|
+
|
|
1991
|
+
### Test Structure
|
|
1992
|
+
|
|
1993
|
+
```text
|
|
1994
|
+
tests/
|
|
1995
|
+
├── _fixtures/ # Shared test fixtures
|
|
1996
|
+
├── api/ # API tests
|
|
1997
|
+
├── asyncValues/ # Async function resolution tests
|
|
1998
|
+
├── syncValues/ # Sync function resolution tests
|
|
1999
|
+
├── cronValues/ # Cron expression tests
|
|
2000
|
+
├── gitVariables/ # Git variable tests
|
|
2001
|
+
├── filePathOverrides/ # File path override tests
|
|
2002
|
+
├── filterTests/ # Filter tests
|
|
2003
|
+
├── hclTests/ # Terraform HCL tests
|
|
2004
|
+
├── iniTests/ # INI format tests
|
|
2005
|
+
├── tomlTests/ # TOML format tests
|
|
2006
|
+
├── jsTests/ # JavaScript file tests
|
|
2007
|
+
└── ... # More test categories
|
|
2008
|
+
```
|
|
2009
|
+
|
|
2010
|
+
### Writing Tests
|
|
2011
|
+
|
|
2012
|
+
Configorama uses the `uvu` test framework. Tests can be run directly with Node.js:
|
|
2013
|
+
|
|
2014
|
+
```bash
|
|
2015
|
+
# Run a single test file
|
|
2016
|
+
node tests/api/api.test.js
|
|
2017
|
+
```
|
|
1074
2018
|
|
|
1075
|
-
|
|
1076
|
-
|
|
2019
|
+
**Example test:**
|
|
2020
|
+
|
|
2021
|
+
```javascript
|
|
2022
|
+
const { test } = require('uvu')
|
|
2023
|
+
const assert = require('uvu/assert')
|
|
2024
|
+
const path = require('path')
|
|
2025
|
+
const configorama = require('../src')
|
|
2026
|
+
|
|
2027
|
+
test('resolves environment variables', async () => {
|
|
2028
|
+
process.env.TEST_VAR = 'test-value'
|
|
2029
|
+
|
|
2030
|
+
const config = await configorama({
|
|
2031
|
+
key: '${env:TEST_VAR}'
|
|
2032
|
+
})
|
|
2033
|
+
|
|
2034
|
+
assert.equal(config.key, 'test-value')
|
|
2035
|
+
|
|
2036
|
+
delete process.env.TEST_VAR
|
|
2037
|
+
})
|
|
2038
|
+
|
|
2039
|
+
test('handles missing env vars with defaults', async () => {
|
|
2040
|
+
const config = await configorama({
|
|
2041
|
+
key: '${env:MISSING_VAR, "default"}'
|
|
2042
|
+
})
|
|
2043
|
+
|
|
2044
|
+
assert.equal(config.key, 'default')
|
|
2045
|
+
})
|
|
2046
|
+
|
|
2047
|
+
test.run()
|
|
2048
|
+
```
|
|
2049
|
+
|
|
2050
|
+
**Test utilities available at `tests/utils.js`:**
|
|
2051
|
+
|
|
2052
|
+
```javascript
|
|
2053
|
+
const { getFixturePath, loadFixture } = require('./tests/utils')
|
|
2054
|
+
|
|
2055
|
+
// Get path to fixture file
|
|
2056
|
+
const fixturePath = getFixturePath('config.yml')
|
|
2057
|
+
|
|
2058
|
+
// Load and parse fixture
|
|
2059
|
+
const fixtureData = loadFixture('config.yml')
|
|
2060
|
+
```
|
|
2061
|
+
|
|
2062
|
+
---
|
|
2063
|
+
|
|
2064
|
+
## Deployment
|
|
2065
|
+
|
|
2066
|
+
### Using with Serverless Framework
|
|
2067
|
+
|
|
2068
|
+
Configorama can be used as a drop-in replacement for the Serverless Framework variable system.
|
|
2069
|
+
|
|
2070
|
+
**serverless.js:**
|
|
2071
|
+
|
|
2072
|
+
```javascript
|
|
1077
2073
|
const path = require('path')
|
|
1078
2074
|
const configorama = require('configorama')
|
|
1079
2075
|
const args = require('minimist')(process.argv.slice(2))
|
|
@@ -1084,73 +2080,752 @@ const yamlFile = path.join(__dirname, 'serverless.config.yml')
|
|
|
1084
2080
|
module.exports = configorama.sync(yamlFile, { options: args })
|
|
1085
2081
|
```
|
|
1086
2082
|
|
|
1087
|
-
|
|
2083
|
+
**serverless.config.yml:**
|
|
1088
2084
|
|
|
1089
|
-
|
|
2085
|
+
```yaml
|
|
2086
|
+
service: my-service
|
|
1090
2087
|
|
|
1091
|
-
|
|
2088
|
+
provider:
|
|
2089
|
+
name: aws
|
|
2090
|
+
runtime: nodejs18.x
|
|
2091
|
+
stage: ${opt:stage, 'dev'}
|
|
2092
|
+
region: ${opt:region, 'us-east-1'}
|
|
1092
2093
|
|
|
1093
|
-
|
|
2094
|
+
# Environment-specific config
|
|
2095
|
+
environment:
|
|
2096
|
+
STAGE: ${opt:stage}
|
|
2097
|
+
DB_HOST: ${env:DB_HOST}
|
|
2098
|
+
API_KEY: ${ssm:/my-service/${opt:stage}/api-key}
|
|
1094
2099
|
|
|
1095
|
-
|
|
2100
|
+
custom:
|
|
2101
|
+
# Load stage-specific config
|
|
2102
|
+
stageConfig: ${file(./config/${opt:stage}.yml)}
|
|
1096
2103
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
2104
|
+
# Git info for tracking
|
|
2105
|
+
deploymentInfo:
|
|
2106
|
+
branch: ${git:branch}
|
|
2107
|
+
commit: ${git:sha1}
|
|
2108
|
+
timestamp: ${timestamp}
|
|
1100
2109
|
|
|
1101
|
-
|
|
2110
|
+
functions:
|
|
2111
|
+
api:
|
|
2112
|
+
handler: handler.api
|
|
2113
|
+
memorySize: ${if(${provider.stage} === 'prod' ? 1024 : 512)}
|
|
2114
|
+
events:
|
|
2115
|
+
- http:
|
|
2116
|
+
path: /
|
|
2117
|
+
method: ANY
|
|
2118
|
+
```
|
|
1102
2119
|
|
|
1103
|
-
|
|
1104
|
-
keyOne:
|
|
1105
|
-
subKey: hi
|
|
2120
|
+
**Deploy:**
|
|
1106
2121
|
|
|
1107
|
-
|
|
1108
|
-
|
|
2122
|
+
```bash
|
|
2123
|
+
# Deploy to dev
|
|
2124
|
+
serverless deploy --stage dev
|
|
1109
2125
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
2126
|
+
# Deploy to production
|
|
2127
|
+
serverless deploy --stage prod --region us-west-2
|
|
2128
|
+
```
|
|
1113
2129
|
|
|
1114
|
-
|
|
2130
|
+
---
|
|
1115
2131
|
|
|
1116
|
-
|
|
1117
|
-
key: ${env:whatever, 2}
|
|
1118
|
-
```
|
|
2132
|
+
### Docker Deployment
|
|
1119
2133
|
|
|
1120
|
-
|
|
2134
|
+
**Dockerfile:**
|
|
1121
2135
|
|
|
1122
|
-
|
|
2136
|
+
```dockerfile
|
|
2137
|
+
FROM node:18-alpine
|
|
1123
2138
|
|
|
1124
|
-
|
|
2139
|
+
WORKDIR /app
|
|
1125
2140
|
|
|
1126
|
-
|
|
2141
|
+
# Copy package files
|
|
2142
|
+
COPY package*.json ./
|
|
1127
2143
|
|
|
1128
|
-
|
|
2144
|
+
# Install dependencies
|
|
2145
|
+
RUN npm ci --only=production
|
|
1129
2146
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
```
|
|
2147
|
+
# Copy application files
|
|
2148
|
+
COPY . .
|
|
1133
2149
|
|
|
1134
|
-
|
|
2150
|
+
# Set environment variables (can be overridden at runtime)
|
|
2151
|
+
ENV NODE_ENV=production
|
|
2152
|
+
ENV STAGE=prod
|
|
1135
2153
|
|
|
2154
|
+
# Run config resolution at build time (optional)
|
|
2155
|
+
# RUN node -e "require('configorama').sync('./config.yml', { options: { stage: process.env.STAGE } })"
|
|
2156
|
+
|
|
2157
|
+
CMD ["node", "index.js"]
|
|
2158
|
+
```
|
|
2159
|
+
|
|
2160
|
+
**docker-compose.yml:**
|
|
2161
|
+
|
|
2162
|
+
```yaml
|
|
2163
|
+
version: '3.8'
|
|
2164
|
+
|
|
2165
|
+
services:
|
|
2166
|
+
app:
|
|
2167
|
+
build: .
|
|
2168
|
+
environment:
|
|
2169
|
+
- NODE_ENV=production
|
|
2170
|
+
- STAGE=prod
|
|
2171
|
+
- DB_HOST=postgres
|
|
2172
|
+
- DB_PASSWORD=${DB_PASSWORD}
|
|
2173
|
+
- API_KEY=${API_KEY}
|
|
2174
|
+
depends_on:
|
|
2175
|
+
- postgres
|
|
2176
|
+
|
|
2177
|
+
postgres:
|
|
2178
|
+
image: postgres:15
|
|
2179
|
+
environment:
|
|
2180
|
+
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
2181
|
+
```
|
|
2182
|
+
|
|
2183
|
+
**Usage:**
|
|
2184
|
+
|
|
2185
|
+
```bash
|
|
2186
|
+
# Build
|
|
2187
|
+
docker build -t myapp .
|
|
2188
|
+
|
|
2189
|
+
# Run with environment variables
|
|
2190
|
+
docker run \
|
|
2191
|
+
-e DB_HOST=mydb.example.com \
|
|
2192
|
+
-e DB_PASSWORD=secret \
|
|
2193
|
+
-e API_KEY=abc123 \
|
|
2194
|
+
myapp
|
|
2195
|
+
```
|
|
2196
|
+
|
|
2197
|
+
---
|
|
2198
|
+
|
|
2199
|
+
### CI/CD Integration
|
|
2200
|
+
|
|
2201
|
+
**GitHub Actions example (.github/workflows/deploy.yml):**
|
|
2202
|
+
|
|
2203
|
+
```yaml
|
|
2204
|
+
name: Deploy
|
|
2205
|
+
|
|
2206
|
+
on:
|
|
2207
|
+
push:
|
|
2208
|
+
branches: [main]
|
|
2209
|
+
|
|
2210
|
+
jobs:
|
|
2211
|
+
deploy:
|
|
2212
|
+
runs-on: ubuntu-latest
|
|
2213
|
+
|
|
2214
|
+
steps:
|
|
2215
|
+
- uses: actions/checkout@v3
|
|
2216
|
+
|
|
2217
|
+
- name: Setup Node.js
|
|
2218
|
+
uses: actions/setup-node@v3
|
|
2219
|
+
with:
|
|
2220
|
+
node-version: '18'
|
|
2221
|
+
|
|
2222
|
+
- name: Install dependencies
|
|
2223
|
+
run: npm ci
|
|
2224
|
+
|
|
2225
|
+
- name: Verify config
|
|
2226
|
+
run: npx configorama config.yml --verify
|
|
2227
|
+
env:
|
|
2228
|
+
STAGE: prod
|
|
2229
|
+
DB_HOST: ${{ secrets.DB_HOST }}
|
|
2230
|
+
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
|
|
2231
|
+
API_KEY: ${{ secrets.API_KEY }}
|
|
2232
|
+
|
|
2233
|
+
- name: Run tests
|
|
2234
|
+
run: npm test
|
|
2235
|
+
|
|
2236
|
+
- name: Deploy to production
|
|
2237
|
+
run: npm run deploy
|
|
2238
|
+
env:
|
|
2239
|
+
STAGE: prod
|
|
2240
|
+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
2241
|
+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
2242
|
+
```
|
|
2243
|
+
|
|
2244
|
+
**GitLab CI example (.gitlab-ci.yml):**
|
|
2245
|
+
|
|
2246
|
+
```yaml
|
|
2247
|
+
stages:
|
|
2248
|
+
- verify
|
|
2249
|
+
- test
|
|
2250
|
+
- deploy
|
|
2251
|
+
|
|
2252
|
+
verify-config:
|
|
2253
|
+
stage: verify
|
|
2254
|
+
image: node:18
|
|
2255
|
+
script:
|
|
2256
|
+
- npm ci
|
|
2257
|
+
- npx configorama config.yml --verify --stage $CI_ENVIRONMENT_NAME
|
|
2258
|
+
variables:
|
|
2259
|
+
STAGE: $CI_ENVIRONMENT_NAME
|
|
2260
|
+
|
|
2261
|
+
test:
|
|
2262
|
+
stage: test
|
|
2263
|
+
image: node:18
|
|
2264
|
+
script:
|
|
2265
|
+
- npm ci
|
|
2266
|
+
- npm test
|
|
2267
|
+
|
|
2268
|
+
deploy-production:
|
|
2269
|
+
stage: deploy
|
|
2270
|
+
image: node:18
|
|
2271
|
+
script:
|
|
2272
|
+
- npm ci
|
|
2273
|
+
- npm run deploy
|
|
2274
|
+
environment:
|
|
2275
|
+
name: production
|
|
2276
|
+
only:
|
|
2277
|
+
- main
|
|
2278
|
+
variables:
|
|
2279
|
+
STAGE: prod
|
|
2280
|
+
```
|
|
2281
|
+
|
|
2282
|
+
---
|
|
2283
|
+
|
|
2284
|
+
## Troubleshooting
|
|
2285
|
+
|
|
2286
|
+
### Common Issues
|
|
2287
|
+
|
|
2288
|
+
**Q: Variable not resolving**
|
|
2289
|
+
|
|
2290
|
+
```yaml
|
|
2291
|
+
# Problem
|
|
2292
|
+
apiKey: ${env:API_KEY}
|
|
2293
|
+
# Error: Environment variable 'API_KEY' not found
|
|
2294
|
+
```
|
|
2295
|
+
|
|
2296
|
+
**Solutions:**
|
|
2297
|
+
1. Ensure environment variable is set: `echo $API_KEY`
|
|
2298
|
+
2. Check variable name spelling
|
|
2299
|
+
3. Use default value: `${env:API_KEY, 'default'}`
|
|
2300
|
+
4. Allow unresolved vars: `allowUnresolvedVariables: ['env']`
|
|
2301
|
+
|
|
2302
|
+
---
|
|
2303
|
+
|
|
2304
|
+
**Q: Circular dependency error**
|
|
2305
|
+
|
|
2306
|
+
```yaml
|
|
2307
|
+
# Problem
|
|
2308
|
+
a: ${self:b}
|
|
2309
|
+
b: ${self:a}
|
|
2310
|
+
# Error: Circular variable dependency detected: b → a → b
|
|
2311
|
+
```
|
|
2312
|
+
|
|
2313
|
+
**Solutions:**
|
|
2314
|
+
1. Restructure config to remove circular reference
|
|
2315
|
+
2. Use intermediate values to break the cycle
|
|
2316
|
+
3. Consider if one value should be a constant instead
|
|
2317
|
+
|
|
2318
|
+
---
|
|
2319
|
+
|
|
2320
|
+
**Q: File not found**
|
|
2321
|
+
|
|
2322
|
+
```yaml
|
|
2323
|
+
# Problem
|
|
2324
|
+
secrets: ${file(./secrets.yml)}
|
|
2325
|
+
# Error: File not found: ./secrets.yml
|
|
2326
|
+
```
|
|
2327
|
+
|
|
2328
|
+
**Solutions:**
|
|
2329
|
+
1. Check file path is correct relative to config file
|
|
2330
|
+
2. Ensure file exists: `ls -la secrets.yml`
|
|
2331
|
+
3. Use absolute path: `${file(/absolute/path/to/secrets.yml)}`
|
|
2332
|
+
4. Add default: `${file(./secrets.yml), {}}`
|
|
2333
|
+
|
|
2334
|
+
---
|
|
2335
|
+
|
|
2336
|
+
**Q: TypeScript file execution fails**
|
|
2337
|
+
|
|
2338
|
+
```yaml
|
|
2339
|
+
# Problem
|
|
2340
|
+
config: ${file(./config.ts)}
|
|
2341
|
+
# Error: Cannot find module 'tsx'
|
|
2342
|
+
```
|
|
2343
|
+
|
|
2344
|
+
**Solutions:**
|
|
2345
|
+
1. Install tsx: `npm install tsx --save-dev`
|
|
2346
|
+
2. Or install ts-node: `npm install ts-node typescript --save-dev`
|
|
2347
|
+
3. Ensure TypeScript file exports correctly: `export = value` or `module.exports = value`
|
|
2348
|
+
|
|
2349
|
+
---
|
|
2350
|
+
|
|
2351
|
+
**Q: HCL parsing fails**
|
|
2352
|
+
|
|
2353
|
+
```yaml
|
|
2354
|
+
# Problem
|
|
2355
|
+
terraform: ${file(./main.tf)}
|
|
2356
|
+
# Error: HCL parsing requires @cdktf/hcl2json
|
|
2357
|
+
```
|
|
2358
|
+
|
|
2359
|
+
**Solutions:**
|
|
2360
|
+
1. Install dependency: `npm install @cdktf/hcl2json`
|
|
2361
|
+
2. Ensure HCL file is valid Terraform syntax
|
|
2362
|
+
3. Check file extension is `.tf`, `.hcl`, or `.tf.json`
|
|
2363
|
+
|
|
2364
|
+
---
|
|
2365
|
+
|
|
2366
|
+
### Debug Mode
|
|
2367
|
+
|
|
2368
|
+
Enable debug mode to see detailed resolution steps:
|
|
2369
|
+
|
|
2370
|
+
**CLI:**
|
|
2371
|
+
|
|
2372
|
+
```bash
|
|
2373
|
+
configorama config.yml --debug
|
|
2374
|
+
```
|
|
2375
|
+
|
|
2376
|
+
**Programmatic:**
|
|
2377
|
+
|
|
2378
|
+
```javascript
|
|
2379
|
+
const config = await configorama('config.yml', {
|
|
2380
|
+
returnMetadata: true
|
|
2381
|
+
})
|
|
2382
|
+
|
|
2383
|
+
// Inspect resolution history
|
|
2384
|
+
console.log(config.resolutionHistory)
|
|
2385
|
+
```
|
|
2386
|
+
|
|
2387
|
+
**Environment variable:**
|
|
2388
|
+
|
|
2389
|
+
```bash
|
|
2390
|
+
DEBUG=configorama:* node app.js
|
|
2391
|
+
```
|
|
2392
|
+
|
|
2393
|
+
**Output example:**
|
|
2394
|
+
|
|
2395
|
+
```text
|
|
2396
|
+
configorama:resolve Resolving variable: ${env:API_KEY}
|
|
2397
|
+
configorama:resolve Type: env, Name: API_KEY
|
|
2398
|
+
configorama:resolve Resolved to: secret-key-123
|
|
2399
|
+
configorama:resolve Resolving variable: ${opt:stage}
|
|
2400
|
+
configorama:resolve Type: opt, Name: stage
|
|
2401
|
+
configorama:resolve Resolved to: prod
|
|
2402
|
+
```
|
|
2403
|
+
|
|
2404
|
+
---
|
|
2405
|
+
|
|
2406
|
+
### Circular Dependencies
|
|
2407
|
+
|
|
2408
|
+
Configorama detects circular dependencies and provides helpful error messages:
|
|
2409
|
+
|
|
2410
|
+
```yaml
|
|
2411
|
+
# Direct cycle
|
|
2412
|
+
a: ${self:b}
|
|
2413
|
+
b: ${self:a}
|
|
2414
|
+
```
|
|
2415
|
+
|
|
2416
|
+
**Error:**
|
|
2417
|
+
```text
|
|
2418
|
+
Circular variable dependency detected: b → a → b
|
|
2419
|
+
|
|
2420
|
+
Resolution path:
|
|
2421
|
+
1. Started resolving 'b'
|
|
2422
|
+
2. Required 'a' (from ${self:b})
|
|
2423
|
+
3. Required 'b' (from ${self:a})
|
|
2424
|
+
4. Circular dependency detected
|
|
2425
|
+
|
|
2426
|
+
To fix this, restructure your config to break the circular reference.
|
|
2427
|
+
```
|
|
2428
|
+
|
|
2429
|
+
**How to fix:**
|
|
2430
|
+
|
|
2431
|
+
1. **Use intermediate values:**
|
|
2432
|
+
|
|
2433
|
+
```yaml
|
|
2434
|
+
# Before (circular)
|
|
2435
|
+
a: ${self:b}
|
|
2436
|
+
b: ${self:a}
|
|
2437
|
+
|
|
2438
|
+
# After (fixed)
|
|
2439
|
+
base: value
|
|
2440
|
+
a: ${self:base}
|
|
2441
|
+
b: ${self:base}
|
|
2442
|
+
```
|
|
2443
|
+
|
|
2444
|
+
2. **Make one value a constant:**
|
|
2445
|
+
|
|
2446
|
+
```yaml
|
|
2447
|
+
# Before (circular)
|
|
2448
|
+
apiUrl: ${self:baseUrl}/api
|
|
2449
|
+
baseUrl: ${self:apiUrl}/v1
|
|
2450
|
+
|
|
2451
|
+
# After (fixed)
|
|
2452
|
+
baseUrl: https://example.com
|
|
2453
|
+
apiUrl: ${self:baseUrl}/api/v1
|
|
2454
|
+
```
|
|
2455
|
+
|
|
2456
|
+
3. **Restructure dependencies:**
|
|
2457
|
+
|
|
2458
|
+
```yaml
|
|
2459
|
+
# Before (circular)
|
|
2460
|
+
database:
|
|
2461
|
+
connectionString: postgres://${database.host}:${database.port}/${database.name}
|
|
2462
|
+
host: ${self:database.connectionString}
|
|
2463
|
+
|
|
2464
|
+
# After (fixed)
|
|
2465
|
+
database:
|
|
2466
|
+
host: localhost
|
|
2467
|
+
port: 5432
|
|
2468
|
+
name: mydb
|
|
2469
|
+
connectionString: postgres://${database.host}:${database.port}/${database.name}
|
|
2470
|
+
```
|
|
2471
|
+
|
|
2472
|
+
---
|
|
2473
|
+
|
|
2474
|
+
## FAQ
|
|
2475
|
+
|
|
2476
|
+
**Q: What happens with circular variable dependencies?**
|
|
2477
|
+
|
|
2478
|
+
Configorama detects circular dependencies and throws a helpful error instead of hanging forever. See [Circular Dependencies](#circular-dependencies) section for examples and fixes.
|
|
2479
|
+
|
|
2480
|
+
---
|
|
2481
|
+
|
|
2482
|
+
**Q: Why should I use this?**
|
|
2483
|
+
|
|
2484
|
+
Never render a stale configuration file again! Configorama ensures your configs are always up-to-date with the latest environment variables, CLI flags, file contents, and custom sources.
|
|
2485
|
+
|
|
2486
|
+
---
|
|
2487
|
+
|
|
2488
|
+
**Q: Does this work with `serverless.yml`?**
|
|
2489
|
+
|
|
2490
|
+
Yes! Use `serverless.js` as your main entry point. See [Using with Serverless Framework](#using-with-serverless-framework) for full example.
|
|
2491
|
+
|
|
2492
|
+
---
|
|
2493
|
+
|
|
2494
|
+
**Q: Can I use this with other frameworks/tools?**
|
|
2495
|
+
|
|
2496
|
+
Yes! Configorama is framework-agnostic. It works with any tool that accepts a JavaScript object or can import a .js file. Examples:
|
|
2497
|
+
|
|
2498
|
+
- **Webpack**: `webpack.config.js`
|
|
2499
|
+
- **Vite**: `vite.config.js`
|
|
2500
|
+
- **Jest**: `jest.config.js`
|
|
2501
|
+
- **ESLint**: `eslint.config.js`
|
|
2502
|
+
- **Docker Compose**: Generate yaml from resolved config
|
|
2503
|
+
- **Kubernetes**: Generate manifests from resolved config
|
|
2504
|
+
|
|
2505
|
+
---
|
|
2506
|
+
|
|
2507
|
+
**Q: How do I handle secrets securely?**
|
|
2508
|
+
|
|
2509
|
+
Best practices:
|
|
2510
|
+
|
|
2511
|
+
1. **Use environment variables:**
|
|
2512
|
+
```yaml
|
|
2513
|
+
apiKey: ${env:API_KEY}
|
|
2514
|
+
```
|
|
2515
|
+
|
|
2516
|
+
2. **Fetch from secret managers:**
|
|
2517
|
+
```yaml
|
|
2518
|
+
secrets: ${file(./fetch-secrets.js)}
|
|
2519
|
+
```
|
|
2520
|
+
|
|
2521
|
+
```javascript
|
|
2522
|
+
// fetch-secrets.js
|
|
2523
|
+
const AWS = require('aws-sdk')
|
|
2524
|
+
const ssm = new AWS.SSM()
|
|
2525
|
+
|
|
2526
|
+
module.exports = async () => {
|
|
2527
|
+
const result = await ssm.getParameter({
|
|
2528
|
+
Name: '/myapp/api-key',
|
|
2529
|
+
WithDecryption: true
|
|
2530
|
+
}).promise()
|
|
2531
|
+
|
|
2532
|
+
return result.Parameter.Value
|
|
2533
|
+
}
|
|
2534
|
+
```
|
|
2535
|
+
|
|
2536
|
+
3. **Never commit secrets to version control**
|
|
2537
|
+
4. **Use `.gitignore` for secret files**
|
|
2538
|
+
5. **Rotate secrets regularly**
|
|
2539
|
+
|
|
2540
|
+
---
|
|
2541
|
+
|
|
2542
|
+
**Q: Can I use variables in variable syntax?**
|
|
2543
|
+
|
|
2544
|
+
Yes! Variables are resolved recursively:
|
|
2545
|
+
|
|
2546
|
+
```yaml
|
|
2547
|
+
stage: prod
|
|
2548
|
+
configFile: config-${stage}.yml
|
|
2549
|
+
config: ${file(${configFile})}
|
|
2550
|
+
# Resolves: ${file(config-prod.yml)}
|
|
2551
|
+
```
|
|
2552
|
+
|
|
2553
|
+
---
|
|
2554
|
+
|
|
2555
|
+
**Q: How do I migrate from Serverless Framework variables?**
|
|
2556
|
+
|
|
2557
|
+
Configorama is mostly compatible with Serverless Framework variable syntax. Key differences:
|
|
2558
|
+
|
|
2559
|
+
1. **Cleaner self-references:**
|
|
2560
|
+
```yaml
|
|
2561
|
+
# Serverless
|
|
2562
|
+
key: ${self:other.key}
|
|
2563
|
+
|
|
2564
|
+
# Configorama (both work)
|
|
2565
|
+
key: ${self:other.key}
|
|
2566
|
+
key: ${other.key}
|
|
2567
|
+
```
|
|
2568
|
+
|
|
2569
|
+
2. **Numbers as defaults:**
|
|
2570
|
+
```yaml
|
|
2571
|
+
# Configorama supports numeric defaults
|
|
2572
|
+
timeout: ${env:TIMEOUT, 30}
|
|
2573
|
+
```
|
|
2574
|
+
|
|
2575
|
+
3. **Additional variable types:**
|
|
2576
|
+
- `${cron()}` - Cron expressions
|
|
2577
|
+
- `${eval()}` - Math expressions
|
|
2578
|
+
- `${if()}` - Conditionals
|
|
2579
|
+
- `${git:}` - Git data
|
|
2580
|
+
|
|
2581
|
+
---
|
|
2582
|
+
|
|
2583
|
+
## Advanced Usage
|
|
2584
|
+
|
|
2585
|
+
### Multi-Stage Resolution
|
|
2586
|
+
|
|
2587
|
+
Resolve configs in multiple stages, allowing external systems to handle remaining variables:
|
|
2588
|
+
|
|
2589
|
+
```javascript
|
|
2590
|
+
// Stage 1: Local resolution (resolve env, opt, file, etc.)
|
|
2591
|
+
const partiallyResolved = await configorama('config.yml', {
|
|
2592
|
+
options: { stage: 'prod' },
|
|
2593
|
+
allowUnresolvedVariables: ['ssm', 'cf'],
|
|
2594
|
+
allowUnknownVariableTypes: ['ssm', 'cf']
|
|
2595
|
+
})
|
|
2596
|
+
|
|
2597
|
+
// Stage 2: External system resolves SSM and CloudFormation refs
|
|
2598
|
+
// (e.g., Serverless Dashboard, AWS CloudFormation, etc.)
|
|
2599
|
+
const fullyResolved = await externalResolver(partiallyResolved)
|
|
2600
|
+
```
|
|
2601
|
+
|
|
2602
|
+
**Use case:** Serverless Framework + Serverless Dashboard workflow.
|
|
2603
|
+
|
|
2604
|
+
---
|
|
2605
|
+
|
|
2606
|
+
### Function Arguments and Context
|
|
2607
|
+
|
|
2608
|
+
Pass dynamic data from your config to JavaScript/TypeScript functions:
|
|
2609
|
+
|
|
2610
|
+
```yaml
|
|
2611
|
+
environment: prod
|
|
2612
|
+
region: us-east-1
|
|
2613
|
+
features:
|
|
2614
|
+
enableMetrics: true
|
|
2615
|
+
|
|
2616
|
+
# Pass resolved config values as arguments
|
|
2617
|
+
secrets: ${file(./get-secrets.js, ${environment}, ${region}, ${features})}
|
|
2618
|
+
```
|
|
2619
|
+
|
|
2620
|
+
**get-secrets.js:**
|
|
2621
|
+
|
|
2622
|
+
```javascript
|
|
2623
|
+
async function getSecrets(env, region, features, ctx) {
|
|
2624
|
+
// Arguments from YAML
|
|
2625
|
+
console.log(env) // 'prod'
|
|
2626
|
+
console.log(region) // 'us-east-1'
|
|
2627
|
+
console.log(features) // { enableMetrics: true }
|
|
2628
|
+
|
|
2629
|
+
// Context (always last argument)
|
|
2630
|
+
console.log(ctx.options) // CLI options
|
|
2631
|
+
console.log(ctx.originalConfig) // Original config
|
|
2632
|
+
console.log(ctx.currentConfig) // Partially resolved config
|
|
2633
|
+
|
|
2634
|
+
// Fetch secrets based on arguments
|
|
2635
|
+
if (env === 'prod') {
|
|
2636
|
+
return await fetchProdSecrets(region)
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
return await fetchDevSecrets()
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
module.exports = getSecrets
|
|
2643
|
+
```
|
|
2644
|
+
|
|
2645
|
+
---
|
|
2646
|
+
|
|
2647
|
+
### Programmatic Usage
|
|
2648
|
+
|
|
2649
|
+
**Custom variable resolver:**
|
|
2650
|
+
|
|
2651
|
+
```javascript
|
|
2652
|
+
const configorama = require('configorama')
|
|
2653
|
+
|
|
2654
|
+
// Add custom AWS SSM resolver
|
|
2655
|
+
const config = await configorama('config.yml', {
|
|
2656
|
+
variableSources: [{
|
|
2657
|
+
type: 'ssm',
|
|
2658
|
+
source: 'remote',
|
|
2659
|
+
syntax: '${ssm:/path}',
|
|
2660
|
+
description: 'AWS Systems Manager Parameter Store',
|
|
2661
|
+
match: /^ssm:/,
|
|
2662
|
+
resolver: async (variable) => {
|
|
2663
|
+
const AWS = require('aws-sdk')
|
|
2664
|
+
const ssm = new AWS.SSM()
|
|
2665
|
+
|
|
2666
|
+
const paramName = variable.replace(/^ssm:/, '')
|
|
2667
|
+
const result = await ssm.getParameter({
|
|
2668
|
+
Name: paramName,
|
|
2669
|
+
WithDecryption: true
|
|
2670
|
+
}).promise()
|
|
2671
|
+
|
|
2672
|
+
return result.Parameter.Value
|
|
2673
|
+
}
|
|
2674
|
+
}]
|
|
2675
|
+
})
|
|
2676
|
+
```
|
|
2677
|
+
|
|
2678
|
+
**Custom filters:**
|
|
2679
|
+
|
|
2680
|
+
```javascript
|
|
2681
|
+
const config = await configorama('config.yml', {
|
|
2682
|
+
filters: {
|
|
2683
|
+
// Custom string transformation
|
|
2684
|
+
slugify: (str) => str.toLowerCase().replace(/\s+/g, '-'),
|
|
2685
|
+
|
|
2686
|
+
// Custom formatting
|
|
2687
|
+
currency: (amount) => `$${parseFloat(amount).toFixed(2)}`,
|
|
2688
|
+
|
|
2689
|
+
// Chained filters work
|
|
2690
|
+
upperSnake: (str) => str.toUpperCase().replace(/\s+/g, '_')
|
|
2691
|
+
}
|
|
2692
|
+
})
|
|
2693
|
+
```
|
|
2694
|
+
|
|
2695
|
+
```yaml
|
|
2696
|
+
# Usage
|
|
2697
|
+
projectName: My Awesome Project
|
|
2698
|
+
slug: ${projectName | slugify} # 'my-awesome-project'
|
|
2699
|
+
|
|
2700
|
+
price: 19.99
|
|
2701
|
+
displayPrice: ${price | currency} # '$19.99'
|
|
2702
|
+
|
|
2703
|
+
constantName: my constant
|
|
2704
|
+
constName: ${constantName | upperSnake} # 'MY_CONSTANT'
|
|
2705
|
+
```
|
|
2706
|
+
|
|
2707
|
+
**Custom functions:**
|
|
2708
|
+
|
|
2709
|
+
```javascript
|
|
2710
|
+
const config = await configorama('config.yml', {
|
|
2711
|
+
functions: {
|
|
2712
|
+
// Timestamp generator
|
|
2713
|
+
timestamp: () => new Date().toISOString(),
|
|
2714
|
+
|
|
2715
|
+
// Random ID generator
|
|
2716
|
+
uuid: () => require('crypto').randomUUID(),
|
|
2717
|
+
|
|
2718
|
+
// Environment-based selector
|
|
2719
|
+
selectByEnv: (prodValue, devValue, env) => {
|
|
2720
|
+
return env === 'prod' ? prodValue : devValue
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
})
|
|
2724
|
+
```
|
|
2725
|
+
|
|
2726
|
+
```yaml
|
|
2727
|
+
# Usage
|
|
2728
|
+
createdAt: ${timestamp()}
|
|
2729
|
+
id: ${uuid()}
|
|
2730
|
+
|
|
2731
|
+
environment: ${opt:stage, 'dev'}
|
|
2732
|
+
timeout: ${selectByEnv(30, 5, ${environment})}
|
|
2733
|
+
```
|
|
2734
|
+
|
|
2735
|
+
---
|
|
2736
|
+
|
|
2737
|
+
## What's New
|
|
2738
|
+
|
|
2739
|
+
How is this different than the Serverless Framework variable system?
|
|
2740
|
+
|
|
2741
|
+
1. **Framework-agnostic** - Use with any tool, not just Serverless Framework
|
|
2742
|
+
|
|
2743
|
+
2. **Pluggable** - Add custom variable syntax and sources easily
|
|
2744
|
+
|
|
2745
|
+
3. **Filters** - Transform values before resolution:
|
|
2746
|
+
```yaml
|
|
2747
|
+
key: ${opt:stage | toUpperCase}
|
|
2748
|
+
```
|
|
2749
|
+
|
|
2750
|
+
4. **Cleaner self-references** - No need for `self:` prefix:
|
|
2751
|
+
```yaml
|
|
2752
|
+
keyOne:
|
|
2753
|
+
subKey: hi
|
|
2754
|
+
|
|
2755
|
+
# Before
|
|
2756
|
+
key: ${self:keyOne.subKey}
|
|
2757
|
+
|
|
2758
|
+
# Now (both work)
|
|
2759
|
+
key: ${keyOne.subKey}
|
|
2760
|
+
key: ${self:keyOne.subKey}
|
|
2761
|
+
```
|
|
2762
|
+
|
|
2763
|
+
5. **Numbers as defaults** - Numeric defaults fully supported:
|
|
2764
|
+
```yaml
|
|
2765
|
+
timeout: ${env:TIMEOUT, 30}
|
|
2766
|
+
port: ${opt:port, 3000}
|
|
2767
|
+
```
|
|
2768
|
+
|
|
2769
|
+
6. **Multiple format support** - TOML, YML, JSON, INI, HCL, etc.
|
|
2770
|
+
|
|
2771
|
+
7. **Built-in functions** - Combine and transform values:
|
|
2772
|
+
```yaml
|
|
2773
|
+
merged: ${merge(${obj1}, ${obj2})}
|
|
2774
|
+
```
|
|
2775
|
+
|
|
2776
|
+
8. **TypeScript support** - Execute TypeScript files directly:
|
|
2777
|
+
```yaml
|
|
2778
|
+
config: ${file(./config.ts)}
|
|
2779
|
+
```
|
|
2780
|
+
|
|
2781
|
+
9. **Conditional expressions** - If/else logic in configs:
|
|
2782
|
+
```yaml
|
|
2783
|
+
memory: ${if(${stage} === 'prod' ? 1024 : 512)}
|
|
2784
|
+
```
|
|
2785
|
+
|
|
2786
|
+
10. **Metadata extraction** - Analyze configs without resolving:
|
|
2787
|
+
```javascript
|
|
2788
|
+
const meta = await configorama.analyze('config.yml')
|
|
1136
2789
|
```
|
|
1137
|
-
${merge('one', 'two')} => 'onetwo'
|
|
1138
|
-
```
|
|
1139
2790
|
|
|
1140
|
-
|
|
2791
|
+
---
|
|
2792
|
+
|
|
2793
|
+
## Alternative Libraries
|
|
2794
|
+
|
|
2795
|
+
- [sls-yaml](https://github.com/01alchemist/sls-yaml) - YAML with variable support
|
|
2796
|
+
- [yaml-boost](https://github.com/blackflux/yaml-boost) - YAML preprocessing
|
|
2797
|
+
- [serverless-merge-config](https://github.com/CruGlobal/serverless-merge-config) - Merge Serverless configs
|
|
2798
|
+
- [serverless-terraform-variables](https://www.npmjs.com/package/serverless-terraform-variables) - Terraform variable support
|
|
1141
2799
|
|
|
1142
|
-
|
|
2800
|
+
---
|
|
1143
2801
|
|
|
1144
2802
|
## Inspiration
|
|
1145
2803
|
|
|
1146
|
-
This is forked
|
|
2804
|
+
This is forked from the [Serverless Framework](https://github.com/serverless/serverless/) variable system.
|
|
1147
2805
|
|
|
1148
2806
|
**Mad props to:**
|
|
1149
2807
|
|
|
1150
2808
|
[erikerikson](https://github.com/erikerikson), [eahefnawy](https://github.com/eahefnawy), [HyperBrain](https://github.com/HyperBrain), [ac360](https://github.com/ac360), [gcphost](https://github.com/gcphost), [pmuens](https://github.com/pmuens), [horike37](https://github.com/horike37), [lorengordon](https://github.com/lorengordon), [AndrewFarley](https://github.com/AndrewFarley), [tobyhede](https://github.com/tobyhede), [johncmckim](https://github.com/johncmckim), [mangas](https://github.com/mangas), [e-e-e](https://github.com/e-e-e), [BasileTrujillo](https://github.com/BasileTrujillo), [miltador](https://github.com/miltador), [sammarks](https://github.com/sammarks), [RafalWilinski](https://github.com/RafalWilinski), [indieisaconcept](https://github.com/indieisaconcept), [svdgraaf](https://github.com/svdgraaf), [infiniteluke](https://github.com/infiniteluke), [j0k3r](https://github.com/j0k3r), [craigw](https://github.com/craigw), [bsdkurt](https://github.com/bsdkurt), [aoskotsky-amplify](https://github.com/aoskotsky-amplify), and all the other folks who contributed to the variable system.
|
|
1151
2809
|
|
|
1152
|
-
Additionally these tools were very helpful
|
|
2810
|
+
**Additionally these tools were very helpful:**
|
|
1153
2811
|
|
|
1154
2812
|
- [yaml-boost](https://github.com/blackflux/yaml-boost)
|
|
1155
2813
|
- [serverless-merge-config](https://github.com/CruGlobal/serverless-merge-config)
|
|
1156
|
-
- [terraform
|
|
2814
|
+
- [serverless-terraform-variables](https://www.npmjs.com/package/serverless-terraform-variables)
|
|
2815
|
+
|
|
2816
|
+
---
|
|
2817
|
+
|
|
2818
|
+
## License
|
|
2819
|
+
|
|
2820
|
+
MIT © [David Wells](https://davidwells.io)
|
|
2821
|
+
|
|
2822
|
+
## Contributing
|
|
2823
|
+
|
|
2824
|
+
Contributions welcome! Please read the [contributing guidelines](CONTRIBUTING.md) first.
|
|
2825
|
+
|
|
2826
|
+
## Support
|
|
2827
|
+
|
|
2828
|
+
- 🐛 [Report bugs](https://github.com/DavidWells/configorama/issues)
|
|
2829
|
+
- 💡 [Request features](https://github.com/DavidWells/configorama/issues)
|
|
2830
|
+
- 📖 [Read the docs](https://github.com/DavidWells/configorama#readme)
|
|
2831
|
+
- 💬 [Join discussions](https://github.com/DavidWells/configorama/discussions)
|