configorama 0.7.2 → 0.9.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 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,45 +756,163 @@ 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
- | `allowUnknownVariables` | boolean | `false` | Allow unknown variable types to pass through (e.g., `${custom:thing}`) |
698
- | `allowUnresolvedVariables` | boolean | `false` | Allow known variable types that can't be resolved to pass through instead of throwing |
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` is deprecated, use `allowUnknownVariables` instead.
765
+ > **Note:** Legacy options `allowUnknownVars`, `allowUnknownVariables`, `allowUnknownParams`, and `allowUnknownFileRefs` are deprecated. Use `allowUnknownVariableTypes` and `allowUnresolvedVariables` instead.
703
766
 
704
- ### allowUnknownVariables
767
+ <details>
768
+ <summary><strong>Migration Guide</strong></summary>
705
769
 
706
- When `allowUnknownVariables: true`, unknown variable types (not registered resolvers) pass through as-is:
770
+ **From legacy options to new API:**
707
771
 
708
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'] }
798
+
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>
806
+
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:
810
+
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
- allowUnknownVariables: true,
817
+ syntax: buildVariableSyntax('{{', '}}'), // Mustache-style: {{env:FOO}}
711
818
  options: { stage: 'dev' }
712
819
  })
713
820
 
714
- // Input: { key: '${ssm:/path/to/secret}' } // ssm: not a registered type
715
- // Output: { key: '${ssm:/path/to/secret}' } // passes through instead of throwing
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
- When `allowUnresolvedVariables: true`, variables that can't be resolved (missing env vars, missing files, etc.) pass through as-is instead of throwing an error:
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}' } // passes through instead of throwing
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
+ // Input: { paramKey: '${param:x}', fileKey: '${file(missing.yml)}' }
874
+ // Output: { paramKey: '${param:x}', fileKey: '${file(missing.yml)}' }
875
+
876
+ // Allow only SPECIFIC types to be unresolved
877
+ const config = await configorama(configFile, {
878
+ allowUnresolvedVariables: ['param', 'file'], // only these pass through
879
+ options: { stage: 'prod' }
880
+ })
881
+ // Input: { key: '${env:MISSING_VAR}', paramKey: '${param:x}', fileKey: '${file(missing.yml)}' }
882
+ // Unresolved ${param:x} and ${file(missing.yml)} pass through but
883
+ // Output error thrown because ${env:MISSING_VAR} throws an error
730
884
  ```
731
885
 
732
- This is useful for multi-stage resolution or when you want to analyze config structure without providing all values.
886
+ This is useful for multi-stage resolution (e.g., Downstream Serverless Dashboard resolves params after local resolution).
887
+
888
+ > **Note:** This option does NOT apply to `self:` or dotProp variables (e.g., `${foo.bar.baz}`). These are local references that configorama fully owns—if they can't be resolved, it's a config error, not something to defer to another system.
733
889
 
734
890
  ## FAQ
735
891
 
892
+ **Q: What happens with circular variable dependencies?**
893
+
894
+ Configorama detects circular dependencies and throws a helpful error instead of hanging forever.
895
+
896
+ ```yml
897
+ # Direct cycle - throws error
898
+ a: ${self:b}
899
+ b: ${self:a}
900
+ # Error: Circular variable dependency detected: b → a → b
901
+
902
+ # Indirect cycle - also detected
903
+ a: ${self:b}
904
+ b: ${self:c}
905
+ c: ${self:a}
906
+ # Error: Circular variable dependency detected: c → a → b → c
907
+
908
+ # Works with shorthand syntax too
909
+ foo:
910
+ bar: ${baz.qux}
911
+ baz:
912
+ qux: ${foo.bar}
913
+ # Error: Circular variable dependency detected: baz.qux → foo.bar → baz.qux
914
+ ```
915
+
736
916
  **Q: Why should I use this?**
737
917
 
738
918
  Never rendering a stale configuration file again!
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
- /** Allow unknown variables to pass through without throwing errors */
21
- allowUnknownVars?: boolean
22
- /** Allow undefined values to pass through without throwing errors */
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "configorama",
3
- "version": "0.7.2",
3
+ "version": "0.9.0",
4
4
  "description": "Variable support for configuration files",
5
5
  "main": "src/index.js",
6
6
  "types": "index.d.ts",
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