configorama 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -13
- package/cli.js +3 -1
- package/index.d.ts +36 -3
- package/package.json +1 -1
- package/src/index.js +15 -2
- package/src/main.js +108 -47
- package/src/resolvers/valueFromFile.js +7 -1
- package/src/resolvers/valueFromParam.js +91 -0
- package/src/resolvers/valueFromParam.test.js +207 -0
- package/src/utils/paths/filePathUtils.js +30 -6
- package/src/utils/paths/filePathUtils.test.js +16 -0
- package/src/utils/strings/splitCsv.js +10 -1
- package/src/utils/variables/variableUtils.js +54 -1
- package/src/utils/variables/variableUtils.test.js +44 -0
- package/types/src/index.d.ts +4 -2
- package/types/src/index.d.ts.map +1 -1
- package/types/src/main.d.ts +18 -0
- package/types/src/main.d.ts.map +1 -1
- package/types/src/resolvers/valueFromFile.d.ts.map +1 -1
- package/types/src/resolvers/valueFromParam.d.ts +19 -0
- package/types/src/resolvers/valueFromParam.d.ts.map +1 -0
- package/types/src/utils/paths/filePathUtils.d.ts +1 -1
- package/types/src/utils/paths/filePathUtils.d.ts.map +1 -1
- package/types/src/utils/strings/splitCsv.d.ts +5 -1
- package/types/src/utils/strings/splitCsv.d.ts.map +1 -1
- package/types/src/utils/variables/variableUtils.d.ts +9 -0
- package/types/src/utils/variables/variableUtils.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -29,14 +29,18 @@ See [tests](https://github.com/DavidWells/configorama/tree/master/tests) for mor
|
|
|
29
29
|
<summary>Click to expand</summary>
|
|
30
30
|
|
|
31
31
|
- [About](#about)
|
|
32
|
-
- [How it works](#how-it-works)
|
|
33
32
|
- [Usage](#usage)
|
|
33
|
+
- [How it works](#how-it-works)
|
|
34
34
|
- [Variable Sources](#variable-sources)
|
|
35
35
|
- [Environment variables](#environment-variables)
|
|
36
36
|
- [CLI option flags](#cli-option-flags)
|
|
37
|
+
- [Parameter values](#parameter-values)
|
|
37
38
|
- [Self references](#self-references)
|
|
38
39
|
- [File references](#file-references)
|
|
39
40
|
- [Sync/Async file references](#syncasync-file-references)
|
|
41
|
+
- [Passing arguments to functions](#passing-arguments-to-functions)
|
|
42
|
+
- [ConfigContext](#configcontext)
|
|
43
|
+
- [Functions without arguments](#functions-without-arguments)
|
|
40
44
|
- [TypeScript file references](#typescript-file-references)
|
|
41
45
|
- [Git references](#git-references)
|
|
42
46
|
- [Cron Values](#cron-values)
|
|
@@ -45,7 +49,11 @@ See [tests](https://github.com/DavidWells/configorama/tree/master/tests) for mor
|
|
|
45
49
|
- [Functions (experimental)](#functions-experimental)
|
|
46
50
|
- [More Examples](#more-examples)
|
|
47
51
|
- [Custom Variable Sources](#custom-variable-sources)
|
|
52
|
+
- [Variable Source Types](#variable-source-types)
|
|
48
53
|
- [Options](#options)
|
|
54
|
+
- [Custom Variable Syntax](#custom-variable-syntax)
|
|
55
|
+
- [allowUnknownVariableTypes](#allowunknownvariabletypes)
|
|
56
|
+
- [allowUnresolvedVariables](#allowunresolvedvariables)
|
|
49
57
|
- [FAQ](#faq)
|
|
50
58
|
- [Whats new](#whats-new)
|
|
51
59
|
- [Alt libs](#alt-libs)
|
|
@@ -136,6 +144,7 @@ console.log(result.resolutionHistory) // Step-by-step resolution for each path
|
|
|
136
144
|
|----------|-----------------------|------------------------|
|
|
137
145
|
| env | ${env:VAR} | Environment variables |
|
|
138
146
|
| opt | ${opt:flag} | CLI option flags |
|
|
147
|
+
| param | ${param:key} | Parameter values |
|
|
139
148
|
| self | ${key} or ${self:key} | Self references |
|
|
140
149
|
| file | ${file(path)} | File references |
|
|
141
150
|
| git | ${git:value} | Git data |
|
|
@@ -168,6 +177,58 @@ foo: ${opt:stage}-hello
|
|
|
168
177
|
foo: ${opt:stage, 'dev'}
|
|
169
178
|
```
|
|
170
179
|
|
|
180
|
+
### Parameter values
|
|
181
|
+
|
|
182
|
+
Access parameter values via `${param:key}`. Parameters follow a resolution hierarchy:
|
|
183
|
+
|
|
184
|
+
1. **CLI params** (`--param="key=value"`) - highest priority
|
|
185
|
+
2. **Stage-specific params** (`stages.<stage>.params`)
|
|
186
|
+
3. **Default params** (`stages.default.params`)
|
|
187
|
+
|
|
188
|
+
```yml
|
|
189
|
+
# Direct parameter reference
|
|
190
|
+
appDomain: ${param:domain}
|
|
191
|
+
|
|
192
|
+
# Parameter with fallback
|
|
193
|
+
apiKey: ${param:apiKey, 'default-api-key'}
|
|
194
|
+
|
|
195
|
+
# Stage-specific parameters defined in config
|
|
196
|
+
stages:
|
|
197
|
+
dev:
|
|
198
|
+
params:
|
|
199
|
+
domain: dev.myapp.com
|
|
200
|
+
dbHost: localhost
|
|
201
|
+
prod:
|
|
202
|
+
params:
|
|
203
|
+
domain: myapp.com
|
|
204
|
+
dbHost: prod-db.myapp.com
|
|
205
|
+
default:
|
|
206
|
+
params:
|
|
207
|
+
domain: default.myapp.com
|
|
208
|
+
dbPort: 3306
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**CLI Usage:**
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Single param
|
|
215
|
+
node app.js --param="domain=example.com"
|
|
216
|
+
|
|
217
|
+
# Multiple params
|
|
218
|
+
node app.js --param="domain=example.com" --param="apiKey=secret123"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Code Usage:**
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
const config = await configorama('config.yml', {
|
|
225
|
+
options: {
|
|
226
|
+
stage: 'prod',
|
|
227
|
+
param: ['domain=cli-override.com', 'apiKey=secret']
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
```
|
|
231
|
+
|
|
171
232
|
### Self references
|
|
172
233
|
|
|
173
234
|
Reference values from other key paths in the same configuration file.
|
|
@@ -682,6 +743,7 @@ The `source` property defines how the config wizard handles each variable type:
|
|
|
682
743
|
|----------|-------------|-------------|
|
|
683
744
|
| `${env:VAR}` | `user` | Environment variables |
|
|
684
745
|
| `${opt:flag}` | `user` | CLI option flags |
|
|
746
|
+
| `${param:key}` | `user` | Parameter values |
|
|
685
747
|
| `${self:key}` | `config` | Self references |
|
|
686
748
|
| `${file(path)}` | `config` | File references |
|
|
687
749
|
| `${text(path)}` | `config` | Raw text file references |
|
|
@@ -694,42 +756,125 @@ The `source` property defines how the config wizard handles each variable type:
|
|
|
694
756
|
| Option | Type | Default | Description |
|
|
695
757
|
|--------|------|---------|-------------|
|
|
696
758
|
| `options` | object | `{}` | CLI options/flags to populate `${opt:xyz}` variables |
|
|
697
|
-
| `
|
|
698
|
-
| `
|
|
759
|
+
| `syntax` | string/RegExp | `${...}` | Custom variable syntax regex pattern |
|
|
760
|
+
| `allowUnknownVariableTypes` | boolean \| string[] | `false` | Allow unknown variable types to pass through (e.g., `${ssm:path}`) |
|
|
761
|
+
| `allowUnresolvedVariables` | boolean \| string[] | `false` | Allow known variable types that can't be resolved to pass through |
|
|
699
762
|
| `allowUndefinedValues` | boolean | `false` | Allow undefined to be an end result |
|
|
700
763
|
| `variableSources` | array | `[]` | Custom variable sources (see above) |
|
|
701
764
|
|
|
702
|
-
> **Note:** `allowUnknownVars`
|
|
765
|
+
> **Note:** Legacy options `allowUnknownVars`, `allowUnknownVariables`, `allowUnknownParams`, and `allowUnknownFileRefs` are deprecated. Use `allowUnknownVariableTypes` and `allowUnresolvedVariables` instead.
|
|
766
|
+
|
|
767
|
+
<details>
|
|
768
|
+
<summary><strong>Migration Guide</strong></summary>
|
|
769
|
+
|
|
770
|
+
**From legacy options to new API:**
|
|
771
|
+
|
|
772
|
+
```js
|
|
773
|
+
// OLD → NEW
|
|
774
|
+
|
|
775
|
+
// Unknown variable types (unregistered resolvers)
|
|
776
|
+
{ allowUnknownVars: true } → { allowUnknownVariableTypes: true }
|
|
777
|
+
{ allowUnknownVariables: true } → { allowUnknownVariableTypes: true }
|
|
778
|
+
|
|
779
|
+
// Unresolved params
|
|
780
|
+
{ allowUnknownParams: true } → { allowUnresolvedVariables: ['param'] }
|
|
781
|
+
|
|
782
|
+
// Unresolved file refs
|
|
783
|
+
{ allowUnknownFileRefs: true } → { allowUnresolvedVariables: ['file'] }
|
|
784
|
+
|
|
785
|
+
// Both params and files
|
|
786
|
+
{ allowUnknownParams: true, allowUnknownFileRefs: true }
|
|
787
|
+
→ { allowUnresolvedVariables: ['param', 'file'] }
|
|
788
|
+
|
|
789
|
+
// All unresolved (env, opt, file, param, etc.)
|
|
790
|
+
{ allowUnresolvedVariables: true } // unchanged, now also accepts arrays
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
**New array syntax allows granular control:**
|
|
794
|
+
|
|
795
|
+
```js
|
|
796
|
+
// Only allow specific unknown types
|
|
797
|
+
{ allowUnknownVariableTypes: ['ssm', 'cf', 's3'] }
|
|
703
798
|
|
|
704
|
-
|
|
799
|
+
// Only allow specific resolver types to fail gracefully
|
|
800
|
+
{ allowUnresolvedVariables: ['param'] } // only params, env/file/opt still throw
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
Legacy options still work but will be removed in a future major version.
|
|
804
|
+
|
|
805
|
+
</details>
|
|
705
806
|
|
|
706
|
-
|
|
807
|
+
### Custom Variable Syntax
|
|
808
|
+
|
|
809
|
+
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:
|
|
707
810
|
|
|
708
811
|
```js
|
|
812
|
+
const configorama = require('configorama')
|
|
813
|
+
const { buildVariableSyntax } = require('configorama')
|
|
814
|
+
|
|
815
|
+
// Using buildVariableSyntax helper (recommended)
|
|
709
816
|
const config = await configorama(configFile, {
|
|
710
|
-
|
|
817
|
+
syntax: buildVariableSyntax('{{', '}}'), // Mustache-style: {{env:FOO}}
|
|
711
818
|
options: { stage: 'dev' }
|
|
712
819
|
})
|
|
713
820
|
|
|
714
|
-
//
|
|
715
|
-
|
|
821
|
+
// Other examples:
|
|
822
|
+
buildVariableSyntax('${{', '}}') // ${{env:FOO}}
|
|
823
|
+
buildVariableSyntax('#{', '}') // #{env:FOO}
|
|
824
|
+
buildVariableSyntax('[[', ']]') // [[env:FOO]]
|
|
825
|
+
buildVariableSyntax('<', '>') // <env:FOO>
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
The `buildVariableSyntax(prefix, suffix, excludePatterns)` function:
|
|
829
|
+
- Automatically excludes suffix characters from the allowed character class (prevents parsing issues)
|
|
830
|
+
- Supports nested variables by excluding `$` and `{` from values
|
|
831
|
+
- Third parameter `excludePatterns` is an array of strings to exclude via negative lookahead (default: `['AWS', 'stageVariables']`)
|
|
832
|
+
|
|
833
|
+
### allowUnknownVariableTypes
|
|
834
|
+
|
|
835
|
+
Controls what happens when encountering unregistered variable types (e.g., `${ssm:path}` when `ssm` isn't a registered resolver).
|
|
836
|
+
|
|
837
|
+
```js
|
|
838
|
+
// Allow ALL unknown types to pass through
|
|
839
|
+
const config = await configorama(configFile, {
|
|
840
|
+
allowUnknownVariableTypes: true,
|
|
841
|
+
options: { stage: 'dev' }
|
|
842
|
+
})
|
|
843
|
+
// Input: { key: '${ssm:/path/to/secret}' }
|
|
844
|
+
// Output: { key: '${ssm:/path/to/secret}' }
|
|
845
|
+
|
|
846
|
+
// Allow only SPECIFIC unknown types
|
|
847
|
+
const config = await configorama(configFile, {
|
|
848
|
+
allowUnknownVariableTypes: ['ssm', 'cf'], // only these pass through
|
|
849
|
+
options: { stage: 'dev' }
|
|
850
|
+
})
|
|
851
|
+
// ${ssm:path} and ${cf:stack.output} pass through
|
|
852
|
+
// ${custom:thing} throws an error
|
|
716
853
|
```
|
|
717
854
|
|
|
718
855
|
### allowUnresolvedVariables
|
|
719
856
|
|
|
720
|
-
|
|
857
|
+
Controls what happens when a known resolver can't find a value (missing env vars, missing files, etc.).
|
|
721
858
|
|
|
722
859
|
```js
|
|
860
|
+
// Allow ALL unresolved variables to pass through
|
|
723
861
|
const config = await configorama(configFile, {
|
|
724
862
|
allowUnresolvedVariables: true,
|
|
725
863
|
options: { stage: 'dev' }
|
|
726
864
|
})
|
|
727
|
-
|
|
728
865
|
// Input: { key: '${env:MISSING_VAR}' }
|
|
729
|
-
// Output: { key: '${env:MISSING_VAR}' }
|
|
866
|
+
// Output: { key: '${env:MISSING_VAR}' }
|
|
867
|
+
|
|
868
|
+
// Allow only SPECIFIC types to be unresolved
|
|
869
|
+
const config = await configorama(configFile, {
|
|
870
|
+
allowUnresolvedVariables: ['param', 'file'], // only these pass through
|
|
871
|
+
options: { stage: 'prod' }
|
|
872
|
+
})
|
|
873
|
+
// Unresolved ${param:x} and ${file(missing.yml)} pass through
|
|
874
|
+
// Unresolved ${env:MISSING} throws an error
|
|
730
875
|
```
|
|
731
876
|
|
|
732
|
-
This is useful for multi-stage resolution
|
|
877
|
+
This is useful for multi-stage resolution (e.g., Serverless Dashboard resolves params after local resolution).
|
|
733
878
|
|
|
734
879
|
## FAQ
|
|
735
880
|
|
package/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ const { makeBox } = require('@davidwells/box-logger')
|
|
|
10
10
|
|
|
11
11
|
// Parse command line arguments
|
|
12
12
|
const argv = minimist(process.argv.slice(2), {
|
|
13
|
-
string: ['output', 'o', 'format', 'f'],
|
|
13
|
+
string: ['output', 'o', 'format', 'f', 'param'],
|
|
14
14
|
boolean: ['help', 'h', 'version', 'v', 'debug', 'allow-unknown', 'allow-undefined', 'list', 'info', 'verify'],
|
|
15
15
|
alias: {
|
|
16
16
|
h: 'help',
|
|
@@ -41,6 +41,7 @@ Options:
|
|
|
41
41
|
-d, --debug Enable debug mode
|
|
42
42
|
-i, --info Show info about the config
|
|
43
43
|
-v, --verify Verify the config
|
|
44
|
+
--param <key=value> Pass parameter values (can be used multiple times)
|
|
44
45
|
--allow-unknown Allow unknown variables to pass through
|
|
45
46
|
--allow-undefined Allow undefined values in the final output
|
|
46
47
|
|
|
@@ -49,6 +50,7 @@ Examples:
|
|
|
49
50
|
configorama --info config.yml
|
|
50
51
|
configorama --format yaml config.json
|
|
51
52
|
configorama --output resolved.json config.yml
|
|
53
|
+
configorama --param="domain=myapp.com" --param="key=value" config.yml
|
|
52
54
|
configorama --allow-unknown config.toml
|
|
53
55
|
`)
|
|
54
56
|
process.exit(0)
|
package/index.d.ts
CHANGED
|
@@ -17,10 +17,43 @@ export interface ConfigoramaSettings {
|
|
|
17
17
|
filters?: Record<string, Function>
|
|
18
18
|
/** Object of custom functions */
|
|
19
19
|
functions?: Record<string, Function>
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
/** Parameters to populate for ${param:xyz} */
|
|
21
|
+
params?: Record<string, any>
|
|
22
|
+
|
|
23
|
+
// === Variable Resolution Options ===
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Allow unknown variable types (unregistered resolvers) to pass through.
|
|
27
|
+
* - `true`: All unknown types pass through (e.g., ${ssm:path}, ${cf:stack})
|
|
28
|
+
* - `false`: Throws on unknown types
|
|
29
|
+
* - `string[]`: Only specified types pass through (e.g., ['ssm', 'cf'])
|
|
30
|
+
*/
|
|
31
|
+
allowUnknownVariableTypes?: boolean | string[]
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Allow known variable types that can't resolve to pass through.
|
|
35
|
+
* - `true`: All unresolved variables pass through
|
|
36
|
+
* - `false`: Throws when resolution fails
|
|
37
|
+
* - `string[]`: Only specified types pass through (e.g., ['param', 'file'])
|
|
38
|
+
*/
|
|
39
|
+
allowUnresolvedVariables?: boolean | string[]
|
|
40
|
+
|
|
41
|
+
/** Allow undefined values as final results */
|
|
23
42
|
allowUndefinedValues?: boolean
|
|
43
|
+
|
|
44
|
+
// === Legacy Options (deprecated, use above instead) ===
|
|
45
|
+
|
|
46
|
+
/** @deprecated Use allowUnknownVariableTypes instead */
|
|
47
|
+
allowUnknownVars?: boolean
|
|
48
|
+
/** @deprecated Use allowUnknownVariableTypes instead */
|
|
49
|
+
allowUnknownVariables?: boolean
|
|
50
|
+
/** @deprecated Use allowUnresolvedVariables: ['param'] instead */
|
|
51
|
+
allowUnknownParams?: boolean
|
|
52
|
+
/** @deprecated Use allowUnresolvedVariables: ['file'] instead */
|
|
53
|
+
allowUnknownFileRefs?: boolean
|
|
54
|
+
|
|
55
|
+
// === Other Options ===
|
|
56
|
+
|
|
24
57
|
/** Values passed into .js config files if user using javascript config */
|
|
25
58
|
dynamicArgs?: object | Function
|
|
26
59
|
/** Return both config and metadata about variables found */
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
const Configorama = require('./main')
|
|
2
2
|
const parsers = require('./parsers')
|
|
3
3
|
const enrichMetadata = require('./utils/parsing/enrichMetadata')
|
|
4
|
-
|
|
5
|
-
module.exports.Configorama = Configorama
|
|
4
|
+
const { buildVariableSyntax } = require('./utils/variables/variableUtils')
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* @typedef {Object} ConfigoramaSettings
|
|
@@ -130,3 +129,17 @@ module.exports.analyze = async (configPathOrObject, settings = {}) => {
|
|
|
130
129
|
* @type {Object}
|
|
131
130
|
*/
|
|
132
131
|
module.exports.format = parsers
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Configorama class for advanced usage
|
|
135
|
+
*/
|
|
136
|
+
module.exports.Configorama = Configorama
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Build variable syntax regex with proper character escaping
|
|
140
|
+
* @param {string} [prefix='${'] - Variable prefix (e.g., '${', '{{', '[[')
|
|
141
|
+
* @param {string} [suffix='}'] - Variable suffix (e.g., '}', '}}', ']]')
|
|
142
|
+
* @param {string[]} [excludePatterns] - Patterns to exclude via negative lookahead
|
|
143
|
+
* @returns {string} Regex source string for use with syntax option
|
|
144
|
+
*/
|
|
145
|
+
module.exports.buildVariableSyntax = buildVariableSyntax
|
package/src/main.js
CHANGED
|
@@ -2,19 +2,16 @@
|
|
|
2
2
|
const os = require('os')
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const fs = require('fs')
|
|
5
|
-
|
|
6
5
|
/* // disable logs to find broken tests
|
|
7
6
|
console.log = () => {}
|
|
8
7
|
// process.exit(1)
|
|
9
8
|
/** */
|
|
10
|
-
|
|
11
9
|
/* External dependencies */
|
|
12
10
|
const promiseFinallyShim = require('promise.prototype.finally').shim()
|
|
13
11
|
const findUp = require('find-up')
|
|
14
12
|
const traverse = require('traverse')
|
|
15
13
|
const dotProp = require('dot-prop')
|
|
16
14
|
const { makeBox, makeStackedBoxes } = require('@davidwells/box-logger')
|
|
17
|
-
|
|
18
15
|
/* Utils - root */
|
|
19
16
|
const {
|
|
20
17
|
isArray, isString, isNumber, isObject, isDate, isRegExp, isFunction,
|
|
@@ -23,71 +20,56 @@ const {
|
|
|
23
20
|
} = require('./utils/lodash')
|
|
24
21
|
const PromiseTracker = require('./utils/PromiseTracker')
|
|
25
22
|
const handleSignalEvents = require('./utils/handleSignalEvents')
|
|
26
|
-
|
|
27
23
|
/* Utils - encoders */
|
|
28
24
|
const { encodeUnknown, decodeUnknown } = require('./utils/encoders/unknown-values')
|
|
29
25
|
const { decodeEncodedValue } = require('./utils/encoders')
|
|
30
|
-
const {
|
|
31
|
-
|
|
26
|
+
const { decodeJsSyntax, hasParenthesesPlaceholder, encodeJsonForVariable } = require('./utils/encoders/js-fixes')
|
|
32
27
|
/* Utils - parsing */
|
|
33
28
|
const enrichMetadata = require('./utils/parsing/enrichMetadata')
|
|
34
29
|
const preProcess = require('./utils/parsing/preProcess')
|
|
35
30
|
const { parseFileContents } = require('./utils/parsing/parse')
|
|
36
31
|
const { mergeByKeys } = require('./utils/parsing/mergeByKeys')
|
|
37
32
|
const { arrayToJsonPath } = require('./utils/parsing/arrayToJsonPath')
|
|
38
|
-
|
|
39
33
|
/* Utils - paths */
|
|
40
34
|
const { normalizePath, extractFilePath, resolveInnerVariables } = require('./utils/paths/filePathUtils')
|
|
41
|
-
const { resolveAlias } = require('./utils/paths/resolveAlias')
|
|
42
|
-
const { resolveFilePathFromMatch } = require('./utils/paths/getFullFilePath')
|
|
43
35
|
const { findLineForKey } = require('./utils/paths/findLineForKey')
|
|
44
|
-
|
|
45
36
|
/* Utils - regex */
|
|
46
|
-
const { combineRegexes, funcRegex
|
|
47
|
-
|
|
37
|
+
const { combineRegexes, funcRegex } = require('./utils/regex')
|
|
48
38
|
/* Utils - strings */
|
|
49
39
|
const formatFunctionArgs = require('./utils/strings/formatFunctionArgs')
|
|
50
|
-
|
|
51
40
|
const { splitByComma } = require('./utils/strings/splitByComma')
|
|
52
41
|
const { splitCsv } = require('./utils/strings/splitCsv')
|
|
53
42
|
const { replaceAll } = require('./utils/strings/replaceAll')
|
|
54
43
|
const { getTextAfterOccurrence, findNestedVariable } = require('./utils/strings/textUtils')
|
|
55
|
-
const {
|
|
56
|
-
|
|
44
|
+
const { ensureQuote, isSurroundedByQuotes, startsWithQuotedPipe } = require('./utils/strings/quoteUtils')
|
|
57
45
|
/* Utils - ui */
|
|
58
46
|
const chalk = require('./utils/ui/chalk')
|
|
59
47
|
const deepLog = require('./utils/ui/deep-log')
|
|
60
48
|
const { logHeader } = require('./utils/ui/logs')
|
|
61
49
|
const { createEditorLink } = require('./utils/ui/createEditorLink')
|
|
62
50
|
const { runConfigWizard, isSensitiveVariable } = require('./utils/ui/configWizard')
|
|
63
|
-
|
|
64
51
|
/* Utils - validation */
|
|
65
52
|
const { warnIfNotFound, isValidValue } = require('./utils/validation/warnIfNotFound')
|
|
66
|
-
|
|
67
53
|
/* Utils - variables */
|
|
68
54
|
const cleanVariable = require('./utils/variables/cleanVariable')
|
|
69
55
|
const appendDeepVariable = require('./utils/variables/appendDeepVariable')
|
|
70
|
-
const { extractVariableWrapper, getFallbackString, verifyVariable } = require('./utils/variables/variableUtils')
|
|
56
|
+
const { extractVariableWrapper, getFallbackString, verifyVariable, buildVariableSyntax } = require('./utils/variables/variableUtils')
|
|
71
57
|
const { findNestedVariables } = require('./utils/variables/findNestedVariables')
|
|
72
|
-
|
|
73
58
|
/* Resolvers */
|
|
74
59
|
const getValueFromString = require('./resolvers/valueFromString')
|
|
75
60
|
const getValueFromNumber = require('./resolvers/valueFromNumber')
|
|
76
61
|
const getValueFromEnv = require('./resolvers/valueFromEnv')
|
|
77
62
|
const getValueFromOptions = require('./resolvers/valueFromOptions')
|
|
63
|
+
const getValueFromParam = require('./resolvers/valueFromParam')
|
|
78
64
|
const getValueFromCron = require('./resolvers/valueFromCron')
|
|
79
65
|
const getValueFromEval = require('./resolvers/valueFromEval')
|
|
80
66
|
const createGitResolver = require('./resolvers/valueFromGit')
|
|
81
67
|
const { getValueFromFile: getValueFromFileResolver } = require('./resolvers/valueFromFile')
|
|
82
|
-
|
|
83
68
|
/* Parsers */
|
|
84
|
-
const YAML = require('./parsers/yaml')
|
|
85
|
-
const TOML = require('./parsers/toml')
|
|
86
|
-
const INI = require('./parsers/ini')
|
|
87
69
|
const JSON5 = require('./parsers/json5')
|
|
88
|
-
|
|
89
70
|
/* Functions */
|
|
90
71
|
const md5Function = require('./functions/md5')
|
|
72
|
+
|
|
91
73
|
/**
|
|
92
74
|
* Maintainer's notes:
|
|
93
75
|
*
|
|
@@ -102,6 +84,7 @@ const md5Function = require('./functions/md5')
|
|
|
102
84
|
* pause population, noting the continued depth to traverse. This motivated "deep" variables.
|
|
103
85
|
* Original issue #4687
|
|
104
86
|
*/
|
|
87
|
+
|
|
105
88
|
const deepRefSyntax = RegExp(/(\${)?deep:\d+(\.[^}]+)*()}?/)
|
|
106
89
|
const deepIndexReplacePattern = new RegExp(/^deep:|(\.[^}]+)*$/g)
|
|
107
90
|
const deepIndexPattern = /deep\:(\d*)/
|
|
@@ -110,8 +93,6 @@ const fileRefSyntax = RegExp(/^file\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" =+]+?)\)/g)
|
|
|
110
93
|
const textRefSyntax = RegExp(/^text\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" =+]+?)\)/g)
|
|
111
94
|
// TODO update file regex ^file\((~?[a-zA-Z0-9._\-\/, ]+?)\)
|
|
112
95
|
// To match file(asyncValue.js, lol) input params
|
|
113
|
-
const envRefSyntax = RegExp(/^env:/g)
|
|
114
|
-
const optRefSyntax = RegExp(/^opt:/g)
|
|
115
96
|
const selfRefSyntax = RegExp(/^self:/g)
|
|
116
97
|
const base64WrapperRegex = /\[_\[([A-Za-z0-9+/=\s]*)\]_\]/g
|
|
117
98
|
const logLines = '─────────────────────────────────────────────────'
|
|
@@ -133,13 +114,13 @@ class Configorama {
|
|
|
133
114
|
const options = opts || {}
|
|
134
115
|
// Set opts to pass into JS file calls
|
|
135
116
|
this.settings = Object.assign({}, {
|
|
136
|
-
// Allow
|
|
137
|
-
|
|
138
|
-
|
|
117
|
+
// Allow unknown ${xyz:...} syntax where xyz is not a registered resolver
|
|
118
|
+
// Can be: false | true | ['ssm', 'cf', ...]
|
|
119
|
+
allowUnknownVariableTypes: false,
|
|
120
|
+
// Allow undefined to be an end result
|
|
139
121
|
allowUndefinedValues: false,
|
|
140
|
-
// Allow unknown file refs to pass through without throwing errors
|
|
141
|
-
allowUnknownFileRefs: false,
|
|
142
122
|
// Allow known variable types that can't be resolved to pass through
|
|
123
|
+
// Can be: false | true | ['param', 'file', 'env', ...]
|
|
143
124
|
allowUnresolvedVariables: false,
|
|
144
125
|
// Return metadata
|
|
145
126
|
returnMetadata: false,
|
|
@@ -147,11 +128,27 @@ class Configorama {
|
|
|
147
128
|
returnPreResolvedVariableDetails: false,
|
|
148
129
|
}, options)
|
|
149
130
|
|
|
150
|
-
// Backward compat: allowUnknownVars ->
|
|
151
|
-
if (options.allowUnknownVars !== undefined && options.
|
|
152
|
-
this.settings.
|
|
131
|
+
// Backward compat: allowUnknownVars -> allowUnknownVariableTypes
|
|
132
|
+
if (options.allowUnknownVars !== undefined && options.allowUnknownVariableTypes === undefined) {
|
|
133
|
+
this.settings.allowUnknownVariableTypes = options.allowUnknownVars
|
|
134
|
+
}
|
|
135
|
+
// Backward compat: allowUnknownVariables -> allowUnknownVariableTypes
|
|
136
|
+
if (options.allowUnknownVariables !== undefined && options.allowUnknownVariableTypes === undefined) {
|
|
137
|
+
this.settings.allowUnknownVariableTypes = options.allowUnknownVariables
|
|
153
138
|
}
|
|
154
139
|
|
|
140
|
+
// Merge legacy allowUnknownParams and allowUnknownFileRefs into allowUnresolvedVariables
|
|
141
|
+
let unresolvedSetting = this.settings.allowUnresolvedVariables
|
|
142
|
+
if (unresolvedSetting !== true) {
|
|
143
|
+
const specificTypes = Array.isArray(unresolvedSetting) ? [...unresolvedSetting] : []
|
|
144
|
+
if (options.allowUnknownParams) specificTypes.push('param')
|
|
145
|
+
if (options.allowUnknownFileRefs) specificTypes.push('file')
|
|
146
|
+
if (specificTypes.length > 0) {
|
|
147
|
+
unresolvedSetting = [...new Set(specificTypes)]
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
this.settings.allowUnresolvedVariables = unresolvedSetting
|
|
151
|
+
|
|
155
152
|
this.filterCache = {}
|
|
156
153
|
|
|
157
154
|
this.foundVariables = []
|
|
@@ -160,8 +157,8 @@ class Configorama {
|
|
|
160
157
|
// Track variable resolutions for metadata (keyed by path)
|
|
161
158
|
this.resolutionTracking = {}
|
|
162
159
|
|
|
163
|
-
const defaultSyntax = '
|
|
164
|
-
|
|
160
|
+
const defaultSyntax = buildVariableSyntax('${', '}', ['AWS', 'stageVariables'])
|
|
161
|
+
|
|
165
162
|
const varSyntax = options.syntax || defaultSyntax
|
|
166
163
|
let varRegex
|
|
167
164
|
if (typeof varSyntax === 'string') {
|
|
@@ -229,6 +226,14 @@ class Configorama {
|
|
|
229
226
|
*/
|
|
230
227
|
getValueFromOptions,
|
|
231
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Parameters
|
|
231
|
+
* Usage:
|
|
232
|
+
* ${param:domain}
|
|
233
|
+
* ${param:key, "fallbackValue"}
|
|
234
|
+
*/
|
|
235
|
+
getValueFromParam,
|
|
236
|
+
|
|
232
237
|
/**
|
|
233
238
|
* Cron expressions
|
|
234
239
|
* Usage:
|
|
@@ -542,6 +547,56 @@ class Configorama {
|
|
|
542
547
|
this.callCount = 0
|
|
543
548
|
}
|
|
544
549
|
|
|
550
|
+
/**
|
|
551
|
+
* Check if unresolved variables of a given type should pass through
|
|
552
|
+
* @param {string} type - The resolver type (e.g., 'param', 'file', 'env')
|
|
553
|
+
* @returns {boolean}
|
|
554
|
+
*/
|
|
555
|
+
isUnresolvedAllowed(type) {
|
|
556
|
+
const setting = this.settings.allowUnresolvedVariables
|
|
557
|
+
if (setting === true) return true
|
|
558
|
+
if (setting === false || setting === undefined) return false
|
|
559
|
+
if (Array.isArray(setting) && setting.includes(type)) return true
|
|
560
|
+
return false
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Extract type prefix from a variable string
|
|
565
|
+
* @param {string} varString - Variable string like 'ssm:path/to/thing' or 'custom:value'
|
|
566
|
+
* @returns {string|null} The type prefix or null if not found
|
|
567
|
+
*/
|
|
568
|
+
extractTypePrefix(varString) {
|
|
569
|
+
if (!varString || typeof varString !== 'string') return null
|
|
570
|
+
const colonIndex = varString.indexOf(':')
|
|
571
|
+
if (colonIndex === -1) return null
|
|
572
|
+
return varString.substring(0, colonIndex)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Check if unknown variable types should pass through
|
|
577
|
+
* @param {string} varString - Variable string like 'ssm:path' or full '${ssm:path}'
|
|
578
|
+
* @returns {boolean}
|
|
579
|
+
*/
|
|
580
|
+
isUnknownTypeAllowed(varString) {
|
|
581
|
+
const setting = this.settings.allowUnknownVariableTypes
|
|
582
|
+
if (setting === true) return true
|
|
583
|
+
if (setting === false || setting === undefined) return false
|
|
584
|
+
if (Array.isArray(setting)) {
|
|
585
|
+
// Extract type prefix from variable string
|
|
586
|
+
// Handle both 'ssm:path' and '${ssm:path}' formats
|
|
587
|
+
let cleanVar = varString
|
|
588
|
+
if (cleanVar.startsWith(this.varPrefix)) {
|
|
589
|
+
cleanVar = cleanVar.slice(this.varPrefix.length)
|
|
590
|
+
}
|
|
591
|
+
if (cleanVar.endsWith(this.varSuffix)) {
|
|
592
|
+
cleanVar = cleanVar.slice(0, -this.varSuffix.length)
|
|
593
|
+
}
|
|
594
|
+
const typePrefix = this.extractTypePrefix(cleanVar)
|
|
595
|
+
if (typePrefix && setting.includes(typePrefix)) return true
|
|
596
|
+
}
|
|
597
|
+
return false
|
|
598
|
+
}
|
|
599
|
+
|
|
545
600
|
// ################
|
|
546
601
|
// ## PUBLIC API ##
|
|
547
602
|
// ################
|
|
@@ -1941,8 +1996,8 @@ class Configorama {
|
|
|
1941
1996
|
for (let i = 0; i < matches.length; i += 1) {
|
|
1942
1997
|
warnIfNotFound(matches[i].variable, results[i], {
|
|
1943
1998
|
patterns: {
|
|
1944
|
-
env:
|
|
1945
|
-
opt:
|
|
1999
|
+
env: getValueFromEnv.match,
|
|
2000
|
+
opt: getValueFromOptions.match,
|
|
1946
2001
|
self: selfRefSyntax,
|
|
1947
2002
|
file: fileRefSyntax,
|
|
1948
2003
|
deep: deepRefSyntax,
|
|
@@ -2380,7 +2435,7 @@ class Configorama {
|
|
|
2380
2435
|
|
|
2381
2436
|
if (nestedVar) {
|
|
2382
2437
|
const fallbackStr = getFallbackString(splitVars, nestedVar)
|
|
2383
|
-
if (!this.
|
|
2438
|
+
if (!this.isUnknownTypeAllowed(nestedVar)) {
|
|
2384
2439
|
verifyVariable(nestedVar, valueObject, this.variableTypes, this.config)
|
|
2385
2440
|
}
|
|
2386
2441
|
|
|
@@ -2864,14 +2919,21 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2864
2919
|
// console.log('nestedVars', nestedVars)
|
|
2865
2920
|
const noNestedVars = nestedVars.length < 2
|
|
2866
2921
|
|
|
2867
|
-
if
|
|
2868
|
-
|
|
2922
|
+
// Check if this unresolved variable type should pass through
|
|
2923
|
+
const isFileRef = variableString.match(fileRefSyntax)
|
|
2924
|
+
const isParamRef = variableString.match(getValueFromParam.match)
|
|
2925
|
+
|
|
2926
|
+
// Params pass through entirely (including fallbacks) for third-party resolution
|
|
2927
|
+
if (isParamRef && this.isUnresolvedAllowed('param')) {
|
|
2869
2928
|
return Promise.resolve(encodeUnknown(propertyString))
|
|
2870
2929
|
}
|
|
2871
2930
|
|
|
2872
|
-
|
|
2931
|
+
const isUnresolvedAllowed =
|
|
2932
|
+
this.settings.allowUnresolvedVariables === true ||
|
|
2933
|
+
(isFileRef && this.isUnresolvedAllowed('file'))
|
|
2934
|
+
|
|
2935
|
+
if (isUnresolvedAllowed) {
|
|
2873
2936
|
// Check if outer expression has fallbacks we can use
|
|
2874
|
-
// valueCount[0] is the primary var, valueCount[1+] are fallbacks
|
|
2875
2937
|
if (valueCount.length > 1) {
|
|
2876
2938
|
const primaryVar = valueCount[0]
|
|
2877
2939
|
// If the unresolvable variableString is used INSIDE the primary var,
|
|
@@ -2880,7 +2942,6 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2880
2942
|
return Promise.resolve(undefined)
|
|
2881
2943
|
}
|
|
2882
2944
|
}
|
|
2883
|
-
// Encode unresolved variable to pass through resolution
|
|
2884
2945
|
return Promise.resolve(encodeUnknown(propertyString))
|
|
2885
2946
|
}
|
|
2886
2947
|
|
|
@@ -3046,7 +3107,7 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
3046
3107
|
// console.log('nestedVar', nestedVar)
|
|
3047
3108
|
|
|
3048
3109
|
if (nestedVar) {
|
|
3049
|
-
if (!this.
|
|
3110
|
+
if (!this.isUnknownTypeAllowed(nestedVar)) {
|
|
3050
3111
|
verifyVariable(nestedVar, valueObject, this.variableTypes, this.config)
|
|
3051
3112
|
}
|
|
3052
3113
|
const fallbackStr = getFallbackString(split, nestedVar)
|
|
@@ -3148,8 +3209,8 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
3148
3209
|
|
|
3149
3210
|
|
|
3150
3211
|
|
|
3151
|
-
/* Pass through unknown
|
|
3152
|
-
if (
|
|
3212
|
+
/* Pass through unknown variable types */
|
|
3213
|
+
if (allowSpecialCase || this.isUnknownTypeAllowed(propertyString)) {
|
|
3153
3214
|
// console.log('allowUnknownVars propertyString', propertyString)
|
|
3154
3215
|
const varMatches = propertyString.match(this.variableSyntax)
|
|
3155
3216
|
let allowUnknownVars = propertyString
|