configorama 0.6.12 → 0.6.13

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.
Files changed (66) hide show
  1. package/README.md +196 -24
  2. package/cli.js +3 -3
  3. package/package.json +1 -1
  4. package/src/index.js +22 -32
  5. package/src/main.js +690 -778
  6. package/src/parsers/yaml.js +3 -47
  7. package/src/resolvers/valueFromCron.js +3 -1
  8. package/src/resolvers/valueFromEnv.js +1 -0
  9. package/src/resolvers/valueFromEval.js +1 -0
  10. package/src/resolvers/valueFromFile.js +394 -0
  11. package/src/resolvers/valueFromGit.js +3 -2
  12. package/src/resolvers/valueFromOptions.js +1 -0
  13. package/src/resolvers/valueFromString.js +2 -1
  14. package/src/sync.js +12 -5
  15. package/src/utils/parsing/arrayToJsonPath.test.js +56 -0
  16. package/src/utils/{enrichMetadata.js → parsing/enrichMetadata.js} +177 -15
  17. package/src/utils/{parse.js → parsing/parse.js} +13 -13
  18. package/src/utils/parsing/preProcess.js +165 -0
  19. package/src/utils/{filePathUtils.js → paths/filePathUtils.js} +3 -2
  20. package/src/utils/paths/findLineForKey.js +47 -0
  21. package/src/utils/paths/findLineForKey.test.js +126 -0
  22. package/src/utils/{getFullFilePath.js → paths/getFullFilePath.js} +22 -26
  23. package/src/utils/{resolveAlias.js → paths/resolveAlias.js} +1 -1
  24. package/src/utils/regex/index.js +23 -1
  25. package/src/utils/resolution/preResolveVariable.js +260 -0
  26. package/src/utils/resolution/preResolveVariable.test.js +98 -0
  27. package/src/utils/strings/bracketMatcher.js +86 -0
  28. package/src/utils/strings/bracketMatcher.test.js +135 -0
  29. package/src/utils/{formatFunctionArgs.js → strings/formatFunctionArgs.js} +3 -2
  30. package/src/utils/strings/formatFunctionArgs.test.js +77 -0
  31. package/src/utils/strings/quoteUtils.js +89 -0
  32. package/src/utils/strings/quoteUtils.test.js +217 -0
  33. package/src/utils/strings/replaceAll.test.js +82 -0
  34. package/src/utils/{splitByComma.js → strings/splitByComma.js} +1 -1
  35. package/src/utils/strings/splitCsv.js +38 -0
  36. package/src/utils/strings/splitCsv.test.js +96 -0
  37. package/src/utils/strings/textUtils.test.js +86 -0
  38. package/src/utils/{configWizard.js → ui/configWizard.js} +177 -38
  39. package/src/utils/{createEditorLink.js → ui/createEditorLink.js} +11 -2
  40. package/src/utils/{logs.js → ui/logs.js} +3 -3
  41. package/src/utils/validation/isValidValue.test.js +64 -0
  42. package/src/utils/validation/warnIfNotFound.js +52 -0
  43. package/src/utils/variables/appendDeepVariable.test.js +41 -0
  44. package/src/utils/{cleanVariable.js → variables/cleanVariable.js} +5 -26
  45. package/src/utils/{find-nested-variables.js → variables/findNestedVariables.js} +2 -2
  46. package/src/utils/{find-nested-variables.test.js → variables/findNestedVariables.test.js} +5 -5
  47. package/src/utils/variables/getVariableType.test.js +109 -0
  48. package/src/utils/variables/variableUtils.test.js +117 -0
  49. package/src/utils/isValidValue.js +0 -8
  50. package/src/utils/splitCsv.js +0 -29
  51. package/src/utils/trimSurroundingQuotes.js +0 -5
  52. /package/src/utils/{arrayToJsonPath.js → parsing/arrayToJsonPath.js} +0 -0
  53. /package/src/utils/{cloudformationSchema.js → parsing/cloudformationSchema.js} +0 -0
  54. /package/src/utils/{mergeByKeys.js → parsing/mergeByKeys.js} +0 -0
  55. /package/src/utils/{filePathUtils.test.js → paths/filePathUtils.test.js} +0 -0
  56. /package/src/utils/{find-project-root.js → paths/findProjectRoot.js} +0 -0
  57. /package/src/utils/{resolveAlias.test.js → paths/resolveAlias.test.js} +0 -0
  58. /package/src/utils/{replaceAll.js → strings/replaceAll.js} +0 -0
  59. /package/src/utils/{splitByComma.test.js → strings/splitByComma.test.js} +0 -0
  60. /package/src/utils/{textUtils.js → strings/textUtils.js} +0 -0
  61. /package/src/utils/{chalk.js → ui/chalk.js} +0 -0
  62. /package/src/utils/{deep-log.js → ui/deep-log.js} +0 -0
  63. /package/src/utils/{appendDeepVariable.js → variables/appendDeepVariable.js} +0 -0
  64. /package/src/utils/{cleanVariable.test.js → variables/cleanVariable.test.js} +0 -0
  65. /package/src/utils/{getVariableType.js → variables/getVariableType.js} +0 -0
  66. /package/src/utils/{variableUtils.js → variables/variableUtils.js} +0 -0
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Warns if a variable value is not found during resolution
3
+ */
4
+ const isEmpty = require('lodash.isempty')
5
+
6
+ function isValidValue(val) {
7
+ if (typeof val === 'object' && (val.hasOwnProperty('__internal_only_flag') || val.hasOwnProperty('__internal_metadata'))) {
8
+ return false
9
+ }
10
+ return val !== null && typeof val !== 'undefined' && !(typeof val === 'object' && isEmpty(val))
11
+ }
12
+
13
+ /**
14
+ * Check if variable resolved to valid value, log warning if not
15
+ * @param {string} variableString - The variable being resolved
16
+ * @param {*} valueToPopulate - The resolved value
17
+ * @param {object} options - Configuration options
18
+ * @param {object} options.patterns - Regex patterns for variable types
19
+ * @param {boolean} options.debug - Whether to log warnings
20
+ * @returns {*} The valueToPopulate unchanged
21
+ */
22
+ function warnIfNotFound(variableString, valueToPopulate, options = {}) {
23
+ const { patterns = {}, debug = false } = options
24
+
25
+ let variableTypeText
26
+ if (patterns.env && variableString.match(patterns.env)) {
27
+ variableTypeText = 'environment variable'
28
+ } else if (patterns.opt && variableString.match(patterns.opt)) {
29
+ variableTypeText = 'option'
30
+ } else if (patterns.self && variableString.match(patterns.self)) {
31
+ variableTypeText = 'config attribute'
32
+ } else if (patterns.file && variableString.match(patterns.file)) {
33
+ variableTypeText = 'file'
34
+ } else if (patterns.deep && variableString.match(patterns.deep)) {
35
+ variableTypeText = 'deep'
36
+ } else if (patterns.text && variableString.match(patterns.text)) {
37
+ variableTypeText = 'text'
38
+ }
39
+
40
+ if (!isValidValue(valueToPopulate)) {
41
+ const notFoundMsg = `No ${variableTypeText} found to satisfy the '\${${variableString}}' variable. Attempting fallback value`
42
+ if (debug) {
43
+ console.log(notFoundMsg)
44
+ }
45
+ }
46
+ return valueToPopulate
47
+ }
48
+
49
+ module.exports = {
50
+ warnIfNotFound,
51
+ isValidValue
52
+ }
@@ -0,0 +1,41 @@
1
+ const { test } = require('uvu')
2
+ const assert = require('uvu/assert')
3
+ const appendDeepVariable = require('./appendDeepVariable')
4
+
5
+ test('appendDeepVariable - should append property to variable', () => {
6
+ const result = appendDeepVariable('${env:VAR}', 'subProp')
7
+ assert.equal(result, '${env:VAR.subProp}')
8
+ })
9
+
10
+ test('appendDeepVariable - should handle nested property', () => {
11
+ const result = appendDeepVariable('${self:config}', 'deep.nested.prop')
12
+ assert.equal(result, '${self:config.deep.nested.prop}')
13
+ })
14
+
15
+ test('appendDeepVariable - should handle variable with existing properties', () => {
16
+ const result = appendDeepVariable('${opt:stage.name}', 'region')
17
+ assert.equal(result, '${opt:stage.name.region}')
18
+ })
19
+
20
+ test('appendDeepVariable - should handle simple variable reference', () => {
21
+ const result = appendDeepVariable('${var}', 'prop')
22
+ assert.equal(result, '${var.prop}')
23
+ })
24
+
25
+ test('appendDeepVariable - should handle property with numbers', () => {
26
+ const result = appendDeepVariable('${obj}', 'prop123')
27
+ assert.equal(result, '${obj.prop123}')
28
+ })
29
+
30
+ test('appendDeepVariable - should handle property with underscores', () => {
31
+ const result = appendDeepVariable('${obj}', 'my_prop')
32
+ assert.equal(result, '${obj.my_prop}')
33
+ })
34
+
35
+ test('appendDeepVariable - should handle property with hyphens', () => {
36
+ const result = appendDeepVariable('${obj}', 'my-prop')
37
+ assert.equal(result, '${obj.my-prop}')
38
+ })
39
+
40
+ // Run all tests
41
+ test.run()
@@ -1,5 +1,5 @@
1
- const { findNestedVariables } = require('./find-nested-variables')
2
- const { functionRegex } = require('./regex')
1
+ const { findNestedVariables } = require('./findNestedVariables')
2
+ const { functionRegex } = require('../regex')
3
3
 
4
4
  const DEBUG = false
5
5
  /**
@@ -84,31 +84,10 @@ module.exports = function cleanVariable(
84
84
  }
85
85
 
86
86
 
87
+ const { findOutermostBraces: findOutermostBracesUtil } = require('../strings/bracketMatcher')
88
+
87
89
  function findOutermostBraces(str) {
88
- const matches = []
89
- let i = 0
90
-
91
- while (i < str.length) {
92
- if (str.substring(i, i + 2) === '${') {
93
- let braceCount = 1
94
- let start = i
95
- i += 2
96
-
97
- while (i < str.length && braceCount > 0) {
98
- if (str[i] === '{') braceCount++
99
- else if (str[i] === '}') braceCount--
100
- i++
101
- }
102
-
103
- if (braceCount === 0) {
104
- matches.push(str.substring(start, i))
105
- }
106
- } else {
107
- i++
108
- }
109
- }
110
-
111
- return matches
90
+ return findOutermostBracesUtil(str, '{', '}', '$')
112
91
  }
113
92
 
114
93
  /**
@@ -1,5 +1,5 @@
1
- const { splitByComma } = require('./splitByComma')
2
- const trimQuotes = require('./trimSurroundingQuotes')
1
+ const { splitByComma } = require('../strings/splitByComma')
2
+ const { trimSurroundingQuotes: trimQuotes } = require('../strings/quoteUtils')
3
3
  const { getVariableType } = require('./getVariableType')
4
4
  const FALLBACK_REGEX = /,\s*/
5
5
  const VAR_MATCH_REGEX = /__VAR_\d+__/
@@ -1,12 +1,12 @@
1
1
  const { test } = require('uvu');
2
2
  const assert = require('uvu/assert');
3
- const { findNestedVariables } = require('./find-nested-variables');
4
- const deepLog = require('./deep-log')
3
+ const { findNestedVariables } = require('./findNestedVariables');
4
+ const deepLog = require('../ui/deep-log')
5
5
 
6
6
  // Import resolvers to build variableTypes array
7
- const getValueFromEnv = require('../resolvers/valueFromEnv')
8
- const getValueFromOptions = require('../resolvers/valueFromOptions')
9
- const getValueFromGit = require('../resolvers/valueFromGit')
7
+ const getValueFromEnv = require('../../resolvers/valueFromEnv')
8
+ const getValueFromOptions = require('../../resolvers/valueFromOptions')
9
+ const getValueFromGit = require('../../resolvers/valueFromGit')
10
10
 
11
11
  // Define the regex pattern as used in the main function
12
12
  const regex = /\${((?!AWS|stageVariables)[ ~:a-zA-Z0-9=+!@#%*<>?._'",|\-\/\(\)\\]+?)}/g;
@@ -0,0 +1,109 @@
1
+ const { test } = require('uvu')
2
+ const assert = require('uvu/assert')
3
+ const { getVariableType } = require('./getVariableType')
4
+
5
+ // Tests without variableTypes array (fallback mode)
6
+ test('getVariableType - should detect colon-based type', () => {
7
+ const result = getVariableType('env:VAR')
8
+ assert.equal(result, 'env')
9
+ })
10
+
11
+ test('getVariableType - should detect opt type', () => {
12
+ const result = getVariableType('opt:stage')
13
+ assert.equal(result, 'options')
14
+ })
15
+
16
+ test('getVariableType - should detect file type', () => {
17
+ const result = getVariableType('file:path/to/file.txt')
18
+ assert.equal(result, 'file')
19
+ })
20
+
21
+ test('getVariableType - should detect text type', () => {
22
+ const result = getVariableType('text:some content')
23
+ assert.equal(result, 'text')
24
+ })
25
+
26
+ test('getVariableType - should detect function type', () => {
27
+ const result = getVariableType('md5(input)')
28
+ assert.equal(result, 'function')
29
+ })
30
+
31
+ test('getVariableType - should return dot.prop for simple reference', () => {
32
+ const result = getVariableType('simple.property')
33
+ assert.equal(result, 'dot.prop')
34
+ })
35
+
36
+ test('getVariableType - should handle variable with ${} wrapper', () => {
37
+ const result = getVariableType('${env:VAR}')
38
+ assert.equal(result, 'env')
39
+ })
40
+
41
+ test('getVariableType - should handle custom type with colon', () => {
42
+ const result = getVariableType('custom:value')
43
+ assert.equal(result, 'custom')
44
+ })
45
+
46
+ test('getVariableType - should return dot.prop for no match', () => {
47
+ const result = getVariableType('noMatch')
48
+ assert.equal(result, 'dot.prop')
49
+ })
50
+
51
+ // Tests with variableTypes array
52
+ test('getVariableType - should match regex in variableTypes', () => {
53
+ const variableTypes = [
54
+ { match: /^env:/, type: 'environment' },
55
+ { match: /^opt:/, type: 'option' }
56
+ ]
57
+ const result = getVariableType('env:VAR', variableTypes)
58
+ assert.equal(result, 'environment')
59
+ })
60
+
61
+ test('getVariableType - should match function matcher', () => {
62
+ const variableTypes = [
63
+ {
64
+ match: (str) => str.startsWith('custom:'),
65
+ type: 'custom-type'
66
+ }
67
+ ]
68
+ const result = getVariableType('custom:value', variableTypes)
69
+ assert.equal(result, 'custom-type')
70
+ })
71
+
72
+ test('getVariableType - should fallback to dot.prop with variableTypes', () => {
73
+ const variableTypes = [
74
+ { match: /^env:/, type: 'environment' }
75
+ ]
76
+ const result = getVariableType('simple.prop', variableTypes)
77
+ assert.equal(result, 'dot.prop')
78
+ })
79
+
80
+ test('getVariableType - should match first matching type', () => {
81
+ const variableTypes = [
82
+ { match: /^env:/, type: 'first' },
83
+ { match: /^env:/, type: 'second' }
84
+ ]
85
+ const result = getVariableType('env:VAR', variableTypes)
86
+ assert.equal(result, 'first')
87
+ })
88
+
89
+ test('getVariableType - should skip types without match property', () => {
90
+ const variableTypes = [
91
+ { type: 'no-match' },
92
+ { match: /^env:/, type: 'env-type' }
93
+ ]
94
+ const result = getVariableType('env:VAR', variableTypes)
95
+ assert.equal(result, 'env-type')
96
+ })
97
+
98
+ test('getVariableType - should handle regex with global flag', () => {
99
+ const variableTypes = [
100
+ { match: /^env:/g, type: 'environment' }
101
+ ]
102
+ const result1 = getVariableType('env:VAR1', variableTypes)
103
+ const result2 = getVariableType('env:VAR2', variableTypes)
104
+ assert.equal(result1, 'environment')
105
+ assert.equal(result2, 'environment')
106
+ })
107
+
108
+ // Run all tests
109
+ test.run()
@@ -0,0 +1,117 @@
1
+ const { test } = require('uvu')
2
+ const assert = require('uvu/assert')
3
+ const { getFallbackString, verifyVariable } = require('./variableUtils')
4
+
5
+ // Tests for getFallbackString
6
+ test('getFallbackString - should reconstruct variable from split array', () => {
7
+ const split = ['env:VAR', 'default']
8
+ const result = getFallbackString(split, 'default')
9
+ assert.equal(result, '${default}')
10
+ })
11
+
12
+ test('getFallbackString - should handle nested variable', () => {
13
+ const split = ['env:VAR', 'env:FALLBACK']
14
+ const result = getFallbackString(split, 'env:FALLBACK')
15
+ assert.equal(result, '${env:FALLBACK}')
16
+ })
17
+
18
+ test('getFallbackString - should include all items after nested var', () => {
19
+ const split = ['env:VAR', 'env:FALLBACK', 'final']
20
+ const result = getFallbackString(split, 'env:FALLBACK')
21
+ assert.equal(result, '${env:FALLBACK, final}')
22
+ })
23
+
24
+ test('getFallbackString - should handle first item as nested var', () => {
25
+ const split = ['env:VAR', 'default']
26
+ const result = getFallbackString(split, 'env:VAR')
27
+ assert.equal(result, '${env:VAR, default}')
28
+ })
29
+
30
+ test('getFallbackString - should clean existing ${} wrappers', () => {
31
+ const split = ['${env:VAR}', 'default']
32
+ // The function only matches if nestedVar equals the array element exactly
33
+ // So it reconstructs from the matched element, not from the beginning
34
+ const result = getFallbackString(split, '${env:VAR}')
35
+ // Since match is found, it creates from '${env:VAR}', 'default' -> ${env:VAR}, default}
36
+ // Then regex cleans wrappers: ${env:VAR}, default}
37
+ assert.equal(result, '${env:VAR}, default}')
38
+ })
39
+
40
+ // Tests for verifyVariable
41
+ test('verifyVariable - should return true for valid regex variable', () => {
42
+ const variableTypes = [
43
+ { match: /^env:/ }
44
+ ]
45
+ const valueObject = { path: ['config', 'var'], originalSource: '${env:VAR}' }
46
+ const result = verifyVariable('env:VAR', valueObject, variableTypes)
47
+ assert.is(result, true)
48
+ })
49
+
50
+ test('verifyVariable - should return true for valid function matcher', () => {
51
+ const variableTypes = [
52
+ { match: (str) => str.startsWith('custom:') }
53
+ ]
54
+ const valueObject = { path: ['config'], originalSource: '${custom:value}' }
55
+ const result = verifyVariable('custom:value', valueObject, variableTypes)
56
+ assert.is(result, true)
57
+ })
58
+
59
+ test('verifyVariable - should return false for non-colon variable', () => {
60
+ const variableTypes = [
61
+ { match: /^env:/ }
62
+ ]
63
+ const valueObject = { path: ['config'], originalSource: '${simple.prop}' }
64
+ const result = verifyVariable('simple.prop', valueObject, variableTypes)
65
+ assert.is(result, false)
66
+ })
67
+
68
+ test('verifyVariable - should throw for invalid colon variable', () => {
69
+ const variableTypes = [
70
+ { match: /^env:/ }
71
+ ]
72
+ const valueObject = { path: ['config', 'var'], originalSource: '${invalid:VAR}' }
73
+
74
+ assert.throws(
75
+ () => verifyVariable('invalid:VAR', valueObject, variableTypes),
76
+ /invalid variable syntax/
77
+ )
78
+ })
79
+
80
+ test('verifyVariable - should include path in error message', () => {
81
+ const variableTypes = [
82
+ { match: /^env:/ }
83
+ ]
84
+ const valueObject = { path: ['config', 'nested', 'var'], originalSource: '${bad:VAR}' }
85
+
86
+ assert.throws(
87
+ () => verifyVariable('bad:VAR', valueObject, variableTypes),
88
+ /config\.nested\.var/
89
+ )
90
+ })
91
+
92
+ test('verifyVariable - should include original source in error', () => {
93
+ const variableTypes = [
94
+ { match: /^env:/ }
95
+ ]
96
+ const valueObject = { path: ['config'], originalSource: 'this is ${bad:VAR}' }
97
+
98
+ assert.throws(
99
+ () => verifyVariable('bad:VAR', valueObject, variableTypes),
100
+ /this is \$\{bad:VAR\}/
101
+ )
102
+ })
103
+
104
+ test('verifyVariable - should match with function and config param', () => {
105
+ const config = { stage: 'prod' }
106
+ const variableTypes = [
107
+ {
108
+ match: (str, cfg) => str.startsWith('stage:') && cfg.stage === 'prod'
109
+ }
110
+ ]
111
+ const valueObject = { path: ['config'], originalSource: '${stage:name}' }
112
+ const result = verifyVariable('stage:name', valueObject, variableTypes, config)
113
+ assert.is(result, true)
114
+ })
115
+
116
+ // Run all tests
117
+ test.run()
@@ -1,8 +0,0 @@
1
- const isEmpty = require('lodash.isempty')
2
-
3
- module.exports = function isValidValue(val) {
4
- if (typeof val === 'object' && (val.hasOwnProperty('__internal_only_flag') || val.hasOwnProperty('__internal_metadata'))) {
5
- return false
6
- }
7
- return val !== null && typeof val !== 'undefined' && !(typeof val === 'object' && isEmpty(val))
8
- }
@@ -1,29 +0,0 @@
1
- /**
2
- * Split a string by comma while preserving quoted content
3
- * @param {string} str - String to split
4
- * @param {string} splitter - Optional custom splitter (defaults to ',')
5
- * @returns {string[]} Array of split strings
6
- */
7
- function splitCsv(str, splitter) {
8
- const splitSyntax = splitter || ','
9
- // Split at comma SPACE ", "
10
- return str.split(splitSyntax).reduce(
11
- (acc, curr) => {
12
- if (acc.isConcatting) {
13
- acc.soFar[acc.soFar.length - 1] += ',' + curr
14
- } else {
15
- acc.soFar.push(curr)
16
- }
17
- if (curr.split('"').length % 2 == 0) {
18
- acc.isConcatting = !acc.isConcatting
19
- }
20
- return acc
21
- },
22
- {
23
- soFar: [],
24
- isConcatting: false,
25
- },
26
- ).soFar
27
- }
28
-
29
- module.exports = { splitCsv }
@@ -1,5 +0,0 @@
1
- module.exports = function trimQuotes(str = '') {
2
- return str
3
- .replace(/^(")([^"\n]*?)(\1)$/, "$2")
4
- .replace(/^(')([^'\n]*?)(\1)$/, "$2")
5
- }
File without changes
File without changes