configorama 0.6.18 → 0.7.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.
Files changed (143) hide show
  1. package/README.md +78 -2
  2. package/cli.js +1 -0
  3. package/index.d.ts +90 -37
  4. package/package.json +8 -2
  5. package/src/index.js +42 -14
  6. package/src/main.js +135 -62
  7. package/src/parsers/index.js +10 -0
  8. package/src/parsers/typescript.js +20 -43
  9. package/src/parsers/yaml.js +35 -2
  10. package/src/resolvers/valueFromFile.js +87 -24
  11. package/src/resolvers/valueFromGit.js +11 -3
  12. package/src/utils/encoders/js-fixes.js +44 -0
  13. package/src/utils/parsing/mergeByKeys.js +4 -3
  14. package/src/utils/parsing/parse.js +4 -2
  15. package/src/utils/parsing/preProcess.js +42 -25
  16. package/src/utils/parsing/preProcess.test.js +214 -0
  17. package/src/utils/paths/getFullFilePath.js +1 -1
  18. package/src/utils/resolution/preResolveVariable.js +1 -0
  19. package/src/utils/strings/quoteUtils.js +2 -2
  20. package/src/utils/strings/splitCsv.js +1 -1
  21. package/src/utils/ui/logs.js +4 -4
  22. package/src/utils/validation/warnIfNotFound.js +3 -3
  23. package/src/utils/variables/findNestedVariables.js +2 -2
  24. package/src/utils/variables/variableUtils.js +43 -0
  25. package/src/utils/variables/variableUtils.test.js +38 -1
  26. package/types/cli.d.ts +3 -0
  27. package/types/cli.d.ts.map +1 -0
  28. package/types/src/functions/md5.d.ts +3 -0
  29. package/types/src/functions/md5.d.ts.map +1 -0
  30. package/types/src/index.d.ts +100 -0
  31. package/types/src/index.d.ts.map +1 -0
  32. package/types/src/main.d.ts +275 -0
  33. package/types/src/main.d.ts.map +1 -0
  34. package/types/src/parsers/esm.d.ts +15 -0
  35. package/types/src/parsers/esm.d.ts.map +1 -0
  36. package/types/src/parsers/hcl.d.ts +1 -0
  37. package/types/src/parsers/hcl.d.ts.map +1 -0
  38. package/types/src/parsers/index.d.ts +18 -0
  39. package/types/src/parsers/index.d.ts.map +1 -0
  40. package/types/src/parsers/ini.d.ts +6 -0
  41. package/types/src/parsers/ini.d.ts.map +1 -0
  42. package/types/src/parsers/json5.d.ts +10 -0
  43. package/types/src/parsers/json5.d.ts.map +1 -0
  44. package/types/src/parsers/toml.d.ts +7 -0
  45. package/types/src/parsers/toml.d.ts.map +1 -0
  46. package/types/src/parsers/typescript.d.ts +15 -0
  47. package/types/src/parsers/typescript.d.ts.map +1 -0
  48. package/types/src/parsers/yaml.d.ts +45 -0
  49. package/types/src/parsers/yaml.d.ts.map +1 -0
  50. package/types/src/resolvers/valueFromCron.d.ts +14 -0
  51. package/types/src/resolvers/valueFromCron.d.ts.map +1 -0
  52. package/types/src/resolvers/valueFromEnv.d.ts +8 -0
  53. package/types/src/resolvers/valueFromEnv.d.ts.map +1 -0
  54. package/types/src/resolvers/valueFromEval.d.ts +7 -0
  55. package/types/src/resolvers/valueFromEval.d.ts.map +1 -0
  56. package/types/src/resolvers/valueFromFile.d.ts +58 -0
  57. package/types/src/resolvers/valueFromFile.d.ts.map +1 -0
  58. package/types/src/resolvers/valueFromGit.d.ts +11 -0
  59. package/types/src/resolvers/valueFromGit.d.ts.map +1 -0
  60. package/types/src/resolvers/valueFromNumber.d.ts +6 -0
  61. package/types/src/resolvers/valueFromNumber.d.ts.map +1 -0
  62. package/types/src/resolvers/valueFromOptions.d.ts +9 -0
  63. package/types/src/resolvers/valueFromOptions.d.ts.map +1 -0
  64. package/types/src/resolvers/valueFromSelf.d.ts +1 -0
  65. package/types/src/resolvers/valueFromSelf.d.ts.map +1 -0
  66. package/types/src/resolvers/valueFromString.d.ts +6 -0
  67. package/types/src/resolvers/valueFromString.d.ts.map +1 -0
  68. package/types/src/sync.d.ts +3 -0
  69. package/types/src/sync.d.ts.map +1 -0
  70. package/types/src/utils/PromiseTracker.d.ts +19 -0
  71. package/types/src/utils/PromiseTracker.d.ts.map +1 -0
  72. package/types/src/utils/encoders/index.d.ts +2 -0
  73. package/types/src/utils/encoders/index.d.ts.map +1 -0
  74. package/types/src/utils/encoders/js-fixes.d.ts +23 -0
  75. package/types/src/utils/encoders/js-fixes.d.ts.map +1 -0
  76. package/types/src/utils/encoders/unknown-values.d.ts +18 -0
  77. package/types/src/utils/encoders/unknown-values.d.ts.map +1 -0
  78. package/types/src/utils/handleSignalEvents.d.ts +3 -0
  79. package/types/src/utils/handleSignalEvents.d.ts.map +1 -0
  80. package/types/src/utils/lodash.d.ts +4 -0
  81. package/types/src/utils/lodash.d.ts.map +1 -0
  82. package/types/src/utils/parsing/arrayToJsonPath.d.ts +5 -0
  83. package/types/src/utils/parsing/arrayToJsonPath.d.ts.map +1 -0
  84. package/types/src/utils/parsing/cloudformationSchema.d.ts +2 -0
  85. package/types/src/utils/parsing/cloudformationSchema.d.ts.map +1 -0
  86. package/types/src/utils/parsing/enrichMetadata.d.ts +17 -0
  87. package/types/src/utils/parsing/enrichMetadata.d.ts.map +1 -0
  88. package/types/src/utils/parsing/mergeByKeys.d.ts +5 -0
  89. package/types/src/utils/parsing/mergeByKeys.d.ts.map +1 -0
  90. package/types/src/utils/parsing/parse.d.ts +54 -0
  91. package/types/src/utils/parsing/parse.d.ts.map +1 -0
  92. package/types/src/utils/parsing/preProcess.d.ts +10 -0
  93. package/types/src/utils/parsing/preProcess.d.ts.map +1 -0
  94. package/types/src/utils/paths/filePathUtils.d.ts +32 -0
  95. package/types/src/utils/paths/filePathUtils.d.ts.map +1 -0
  96. package/types/src/utils/paths/findLineForKey.d.ts +12 -0
  97. package/types/src/utils/paths/findLineForKey.d.ts.map +1 -0
  98. package/types/src/utils/paths/findProjectRoot.d.ts +2 -0
  99. package/types/src/utils/paths/findProjectRoot.d.ts.map +1 -0
  100. package/types/src/utils/paths/getFullFilePath.d.ts +25 -0
  101. package/types/src/utils/paths/getFullFilePath.d.ts.map +1 -0
  102. package/types/src/utils/paths/resolveAlias.d.ts +14 -0
  103. package/types/src/utils/paths/resolveAlias.d.ts.map +1 -0
  104. package/types/src/utils/regex/index.d.ts +14 -0
  105. package/types/src/utils/regex/index.d.ts.map +1 -0
  106. package/types/src/utils/resolution/preResolveVariable.d.ts +51 -0
  107. package/types/src/utils/resolution/preResolveVariable.d.ts.map +1 -0
  108. package/types/src/utils/strings/bracketMatcher.d.ts +25 -0
  109. package/types/src/utils/strings/bracketMatcher.d.ts.map +1 -0
  110. package/types/src/utils/strings/formatFunctionArgs.d.ts +3 -0
  111. package/types/src/utils/strings/formatFunctionArgs.d.ts.map +1 -0
  112. package/types/src/utils/strings/quoteUtils.d.ts +31 -0
  113. package/types/src/utils/strings/quoteUtils.d.ts.map +1 -0
  114. package/types/src/utils/strings/replaceAll.d.ts +9 -0
  115. package/types/src/utils/strings/replaceAll.d.ts.map +1 -0
  116. package/types/src/utils/strings/splitByComma.d.ts +2 -0
  117. package/types/src/utils/strings/splitByComma.d.ts.map +1 -0
  118. package/types/src/utils/strings/splitCsv.d.ts +10 -0
  119. package/types/src/utils/strings/splitCsv.d.ts.map +1 -0
  120. package/types/src/utils/strings/textUtils.d.ts +15 -0
  121. package/types/src/utils/strings/textUtils.d.ts.map +1 -0
  122. package/types/src/utils/ui/chalk.d.ts +70 -0
  123. package/types/src/utils/ui/chalk.d.ts.map +1 -0
  124. package/types/src/utils/ui/configWizard.d.ts +67 -0
  125. package/types/src/utils/ui/configWizard.d.ts.map +1 -0
  126. package/types/src/utils/ui/createEditorLink.d.ts +11 -0
  127. package/types/src/utils/ui/createEditorLink.d.ts.map +1 -0
  128. package/types/src/utils/ui/deep-log.d.ts +3 -0
  129. package/types/src/utils/ui/deep-log.d.ts.map +1 -0
  130. package/types/src/utils/ui/logs.d.ts +2 -0
  131. package/types/src/utils/ui/logs.d.ts.map +1 -0
  132. package/types/src/utils/validation/warnIfNotFound.d.ts +15 -0
  133. package/types/src/utils/validation/warnIfNotFound.d.ts.map +1 -0
  134. package/types/src/utils/variables/appendDeepVariable.d.ts +3 -0
  135. package/types/src/utils/variables/appendDeepVariable.d.ts.map +1 -0
  136. package/types/src/utils/variables/cleanVariable.d.ts +3 -0
  137. package/types/src/utils/variables/cleanVariable.d.ts.map +1 -0
  138. package/types/src/utils/variables/findNestedVariables.d.ts +23 -0
  139. package/types/src/utils/variables/findNestedVariables.d.ts.map +1 -0
  140. package/types/src/utils/variables/getVariableType.d.ts +8 -0
  141. package/types/src/utils/variables/getVariableType.d.ts.map +1 -0
  142. package/types/src/utils/variables/variableUtils.d.ts +21 -0
  143. package/types/src/utils/variables/variableUtils.d.ts.map +1 -0
@@ -3,16 +3,27 @@
3
3
  * and escape variables inside help() filter arguments
4
4
  */
5
5
  const { splitByComma } = require('../strings/splitByComma')
6
+ const { extractVariableWrapper } = require('../variables/variableUtils')
6
7
 
7
8
  /**
8
9
  * Preprocess config to fix malformed fallback references
9
10
  * @param {Object} configObject - The parsed configuration object
10
11
  * @param {RegExp} variableSyntax - The variable syntax regex to use
12
+ * @param {Array} [variableTypes] - Array of variable type definitions with type/prefix fields
11
13
  * @returns {Object} The preprocessed configuration object
12
14
  */
13
- function preProcess(configObject, variableSyntax) {
14
- // Known reference prefixes that should be wrapped in ${}
15
- const refPrefixes = ['self:', 'opt:', 'env:', 'file:', 'text:', 'deep:']
15
+ function preProcess(configObject, variableSyntax, variableTypes) {
16
+ // Extract prefix/suffix from variable syntax for reconstructing variables
17
+ const { prefix: varPrefix, suffix: varSuffix } = variableSyntax
18
+ ? extractVariableWrapper(variableSyntax.source)
19
+ : { prefix: '${', suffix: '}' }
20
+
21
+ // Extract reference prefixes from variable types, or use defaults
22
+ const refPrefixes = variableTypes && variableTypes.length > 0
23
+ ? variableTypes
24
+ .map(v => (v.prefix || v.type) + ':')
25
+ .filter(p => p !== 'dot.prop:' && p !== 'string:' && p !== 'number:')
26
+ : ['self:', 'opt:', 'env:', 'file:', 'text:', 'deep:']
16
27
 
17
28
  /**
18
29
  * Escape variables inside help() filter arguments so main resolver skips them
@@ -52,31 +63,37 @@ function preProcess(configObject, variableSyntax) {
52
63
  let changed = true
53
64
 
54
65
  // Keep iterating until no more changes (to handle nested variables)
66
+ const prefixLen = varPrefix.length
67
+ const suffixLen = varSuffix.length
68
+
55
69
  while (changed) {
56
70
  changed = false
57
71
 
58
- // Find innermost ${...} blocks (ones that don't contain other ${)
72
+ // Find innermost variable blocks (ones that don't contain other variables)
59
73
  let i = 0
60
74
  while (i < result.length) {
61
- if (result[i] === '$' && result[i + 1] === '{') {
75
+ if (result.substring(i, i + prefixLen) === varPrefix) {
62
76
  const start = i
63
- let braceCount = 1
64
- let j = i + 2
65
-
66
- // Find the matching closing brace by counting { and }
67
- while (j < result.length && braceCount > 0) {
68
- if (result[j] === '{') {
69
- braceCount++
70
- } else if (result[j] === '}') {
71
- braceCount--
77
+ let depth = 1
78
+ let j = i + prefixLen
79
+
80
+ // Find the matching suffix by counting full prefix/suffix occurrences
81
+ while (j < result.length && depth > 0) {
82
+ if (result.substring(j, j + prefixLen) === varPrefix) {
83
+ depth++
84
+ j += prefixLen
85
+ } else if (result.substring(j, j + suffixLen) === varSuffix) {
86
+ depth--
87
+ if (depth > 0) j += suffixLen
88
+ } else {
89
+ j++
72
90
  }
73
- j++
74
91
  }
75
92
 
76
- if (braceCount === 0) {
77
- const end = j
93
+ if (depth === 0) {
94
+ const end = j + suffixLen
78
95
  const match = result.substring(start, end)
79
- const content = result.substring(start + 2, end - 1)
96
+ const content = result.substring(start + prefixLen, end - suffixLen)
80
97
 
81
98
  // Only process if there's a comma (indicating fallback syntax)
82
99
  if (content.includes(',')) {
@@ -84,10 +101,10 @@ function preProcess(configObject, variableSyntax) {
84
101
  const parts = splitByComma(content, variableSyntax)
85
102
 
86
103
  if (parts.length > 1) {
87
- // Check if the first part has nested ${} - if so, skip this (process inner ones first)
104
+ // Check if the first part has nested variables - if so, skip this (process inner ones first)
88
105
  const firstPart = parts[0]
89
- if (firstPart.includes('${')) {
90
- i = start + 2 // Move past ${ to find inner variables
106
+ if (firstPart.includes(varPrefix)) {
107
+ i = start + prefixLen // Move past prefix to find inner variables
91
108
  continue
92
109
  }
93
110
 
@@ -101,16 +118,16 @@ function preProcess(configObject, variableSyntax) {
101
118
 
102
119
  // Check if this looks like a reference but is not wrapped
103
120
  const looksLikeRef = refPrefixes.some(prefix => trimmed.startsWith(prefix))
104
- const alreadyWrapped = trimmed.startsWith('${') && trimmed.endsWith('}')
121
+ const alreadyWrapped = trimmed.startsWith(varPrefix) && trimmed.endsWith(varSuffix)
105
122
 
106
123
  if (looksLikeRef && !alreadyWrapped) {
107
- return ` \${${trimmed}}`
124
+ return ` ${varPrefix}${trimmed}${varSuffix}`
108
125
  }
109
126
 
110
127
  return ` ${trimmed}`
111
128
  })
112
129
 
113
- const replacement = `\${${fixed.join(',')}}`
130
+ const replacement = `${varPrefix}${fixed.join(',')}${varSuffix}`
114
131
  if (replacement !== match) {
115
132
  result = result.substring(0, start) + replacement + result.substring(end)
116
133
  changed = true
@@ -119,7 +136,7 @@ function preProcess(configObject, variableSyntax) {
119
136
  }
120
137
  }
121
138
 
122
- i = start + 2 // Move past ${ to continue searching for nested variables
139
+ i = start + prefixLen // Move past prefix to continue searching for nested variables
123
140
  } else {
124
141
  i++
125
142
  }
@@ -0,0 +1,214 @@
1
+ // Tests for preProcess utility (fixFallbacksInString)
2
+
3
+ const { test } = require('uvu')
4
+ const assert = require('uvu/assert')
5
+ const preProcess = require('./preProcess')
6
+
7
+ // Default ${} syntax
8
+ const defaultSyntax = /\$\{([ ~:a-zA-Z0-9._\\'",\-\/\(\)]+?)\}/g
9
+
10
+ // Custom syntaxes
11
+ const doubleBraceSyntax = /\$\{\{([ ~:a-zA-Z0-9._\\'",\-\/\(\)]+?)\}\}/g
12
+ const hashSyntax = /\#\{([ ~:a-zA-Z0-9._\\'",\-\/\(\)]+?)\}/g
13
+ const angleSyntax = /\<([ ~:a-zA-Z0-9._\\'",\-\/\(\)]+?)\>/g
14
+
15
+ // Tests for fixFallbacksInString with default ${} syntax
16
+ test('fixFallbacksInString - wraps unwrapped self: fallback', () => {
17
+ const input = { key: '${opt:missing, self:fallback}' }
18
+ const result = preProcess(input, defaultSyntax)
19
+ assert.is(result.key, '${opt:missing, ${self:fallback}}')
20
+ })
21
+
22
+ test('fixFallbacksInString - wraps unwrapped env: fallback', () => {
23
+ const input = { key: '${opt:missing, env:FALLBACK}' }
24
+ const result = preProcess(input, defaultSyntax)
25
+ assert.is(result.key, '${opt:missing, ${env:FALLBACK}}')
26
+ })
27
+
28
+ test('fixFallbacksInString - wraps unwrapped opt: fallback', () => {
29
+ const input = { key: '${self:missing, opt:fallback}' }
30
+ const result = preProcess(input, defaultSyntax)
31
+ assert.is(result.key, '${self:missing, ${opt:fallback}}')
32
+ })
33
+
34
+ test('fixFallbacksInString - wraps unwrapped file: fallback', () => {
35
+ const input = { key: '${opt:missing, file:./config.json}' }
36
+ const result = preProcess(input, defaultSyntax)
37
+ assert.is(result.key, '${opt:missing, ${file:./config.json}}')
38
+ })
39
+
40
+ test('fixFallbacksInString - leaves already wrapped fallback alone', () => {
41
+ const input = { key: '${opt:missing, ${self:fallback}}' }
42
+ const result = preProcess(input, defaultSyntax)
43
+ assert.is(result.key, '${opt:missing, ${self:fallback}}')
44
+ })
45
+
46
+ test('fixFallbacksInString - leaves string fallback alone', () => {
47
+ const input = { key: '${opt:missing, "default"}' }
48
+ const result = preProcess(input, defaultSyntax)
49
+ assert.is(result.key, '${opt:missing, "default"}')
50
+ })
51
+
52
+ test('fixFallbacksInString - leaves numeric fallback alone', () => {
53
+ const input = { key: '${opt:missing, 42}' }
54
+ const result = preProcess(input, defaultSyntax)
55
+ assert.is(result.key, '${opt:missing, 42}')
56
+ })
57
+
58
+ test('fixFallbacksInString - handles multiple fallbacks', () => {
59
+ const input = { key: '${opt:missing, self:first, env:second}' }
60
+ const result = preProcess(input, defaultSyntax)
61
+ assert.is(result.key, '${opt:missing, ${self:first}, ${env:second}}')
62
+ })
63
+
64
+ test('fixFallbacksInString - handles nested variables in primary', () => {
65
+ const input = { key: '${file(./config.${opt:stage}.json), "default"}' }
66
+ const result = preProcess(input, defaultSyntax)
67
+ assert.is(result.key, '${file(./config.${opt:stage}.json), "default"}')
68
+ })
69
+
70
+ // Tests for ${{}} syntax
71
+ test('fixFallbacksInString - ${{}} syntax wraps unwrapped fallback', () => {
72
+ const input = { key: '${{opt:missing, self:fallback}}' }
73
+ const result = preProcess(input, doubleBraceSyntax)
74
+ assert.is(result.key, '${{opt:missing, ${{self:fallback}}}}')
75
+ })
76
+
77
+ test('fixFallbacksInString - ${{}} syntax leaves wrapped fallback alone', () => {
78
+ const input = { key: '${{opt:missing, ${{self:fallback}}}}' }
79
+ const result = preProcess(input, doubleBraceSyntax)
80
+ assert.is(result.key, '${{opt:missing, ${{self:fallback}}}}')
81
+ })
82
+
83
+ test('fixFallbacksInString - ${{}} syntax leaves string fallback alone', () => {
84
+ const input = { key: '${{opt:missing, "default"}}' }
85
+ const result = preProcess(input, doubleBraceSyntax)
86
+ assert.is(result.key, '${{opt:missing, "default"}}')
87
+ })
88
+
89
+ // Tests for #{} syntax
90
+ test('fixFallbacksInString - #{} syntax wraps unwrapped fallback', () => {
91
+ const input = { key: '#{opt:missing, self:fallback}' }
92
+ const result = preProcess(input, hashSyntax)
93
+ assert.is(result.key, '#{opt:missing, #{self:fallback}}')
94
+ })
95
+
96
+ test('fixFallbacksInString - #{} syntax leaves wrapped fallback alone', () => {
97
+ const input = { key: '#{opt:missing, #{self:fallback}}' }
98
+ const result = preProcess(input, hashSyntax)
99
+ assert.is(result.key, '#{opt:missing, #{self:fallback}}')
100
+ })
101
+
102
+ test('fixFallbacksInString - #{} syntax handles multiple fallbacks', () => {
103
+ const input = { key: '#{opt:missing, env:FIRST, self:second}' }
104
+ const result = preProcess(input, hashSyntax)
105
+ assert.is(result.key, '#{opt:missing, #{env:FIRST}, #{self:second}}')
106
+ })
107
+
108
+ // Tests for <> syntax
109
+ test('fixFallbacksInString - <> syntax wraps unwrapped fallback', () => {
110
+ const input = { key: '<opt:missing, self:fallback>' }
111
+ const result = preProcess(input, angleSyntax)
112
+ assert.is(result.key, '<opt:missing, <self:fallback>>')
113
+ })
114
+
115
+ test('fixFallbacksInString - <> syntax leaves wrapped fallback alone', () => {
116
+ const input = { key: '<opt:missing, <self:fallback>>' }
117
+ const result = preProcess(input, angleSyntax)
118
+ assert.is(result.key, '<opt:missing, <self:fallback>>')
119
+ })
120
+
121
+ test('fixFallbacksInString - <> syntax leaves string fallback alone', () => {
122
+ const input = { key: '<opt:missing, "default">' }
123
+ const result = preProcess(input, angleSyntax)
124
+ assert.is(result.key, '<opt:missing, "default">')
125
+ })
126
+
127
+ // Edge cases
128
+ test('fixFallbacksInString - handles deeply nested objects', () => {
129
+ const input = {
130
+ level1: {
131
+ level2: {
132
+ key: '${opt:missing, self:fallback}'
133
+ }
134
+ }
135
+ }
136
+ const result = preProcess(input, defaultSyntax)
137
+ assert.is(result.level1.level2.key, '${opt:missing, ${self:fallback}}')
138
+ })
139
+
140
+ test('fixFallbacksInString - handles arrays', () => {
141
+ const input = {
142
+ items: [
143
+ '${opt:one, self:fallback1}',
144
+ '${opt:two, self:fallback2}'
145
+ ]
146
+ }
147
+ const result = preProcess(input, defaultSyntax)
148
+ assert.is(result.items[0], '${opt:one, ${self:fallback1}}')
149
+ assert.is(result.items[1], '${opt:two, ${self:fallback2}}')
150
+ })
151
+
152
+ test('fixFallbacksInString - handles no variable syntax', () => {
153
+ const input = { key: 'just a plain string' }
154
+ const result = preProcess(input, defaultSyntax)
155
+ assert.is(result.key, 'just a plain string')
156
+ })
157
+
158
+ test('fixFallbacksInString - handles null variableSyntax', () => {
159
+ const input = { key: '${opt:missing, self:fallback}' }
160
+ const result = preProcess(input, null)
161
+ // Without syntax, should use defaults
162
+ assert.is(result.key, '${opt:missing, ${self:fallback}}')
163
+ })
164
+
165
+ // Tests with explicit variableTypes
166
+ const defaultVariableTypes = [
167
+ { type: 'env' },
168
+ { type: 'options', prefix: 'opt' },
169
+ { type: 'self', prefix: 'self' },
170
+ { type: 'file', prefix: 'file' },
171
+ { type: 'text', prefix: 'text' },
172
+ { type: 'deep' },
173
+ ]
174
+
175
+ test('fixFallbacksInString - uses variableTypes to determine prefixes', () => {
176
+ const input = { key: '${opt:missing, self:fallback}' }
177
+ const result = preProcess(input, defaultSyntax, defaultVariableTypes)
178
+ assert.is(result.key, '${opt:missing, ${self:fallback}}')
179
+ })
180
+
181
+ test('fixFallbacksInString - custom variableTypes with custom prefix', () => {
182
+ const customTypes = [
183
+ { type: 'ssm', prefix: 'ssm' },
184
+ { type: 'secret', prefix: 'secret' },
185
+ ]
186
+ const input = { key: '${opt:missing, ssm:/path/to/param}' }
187
+ const result = preProcess(input, defaultSyntax, customTypes)
188
+ assert.is(result.key, '${opt:missing, ${ssm:/path/to/param}}')
189
+ })
190
+
191
+ test('fixFallbacksInString - custom variableTypes wraps secret: fallback', () => {
192
+ const customTypes = [
193
+ { type: 'ssm', prefix: 'ssm' },
194
+ { type: 'secret', prefix: 'secret' },
195
+ { type: 'self', prefix: 'self' },
196
+ ]
197
+ const input = { key: '${self:missing, secret:API_KEY}' }
198
+ const result = preProcess(input, defaultSyntax, customTypes)
199
+ assert.is(result.key, '${self:missing, ${secret:API_KEY}}')
200
+ })
201
+
202
+ test('fixFallbacksInString - ignores dot.prop and string types in prefixes', () => {
203
+ const typesWithDotProp = [
204
+ { type: 'env' },
205
+ { type: 'dot.prop' },
206
+ { type: 'string' },
207
+ { type: 'self', prefix: 'self' },
208
+ ]
209
+ const input = { key: '${env:MISSING, self:fallback}' }
210
+ const result = preProcess(input, defaultSyntax, typesWithDotProp)
211
+ assert.is(result.key, '${env:MISSING, ${self:fallback}}')
212
+ })
213
+
214
+ test.run()
@@ -40,7 +40,7 @@ function getFullPath(fileString, cwd) {
40
40
  * @param {string} matchedFileString - The matched file string (e.g., "file(path/to/file.js)")
41
41
  * @param {RegExp} syntax - The regex pattern used to match the file string (e.g., fileRefSyntax or textRefSyntax)
42
42
  * @param {string} configPath - The base directory path for resolving relative paths
43
- * @returns {{fullFilePath: string|null, resolvedPath: string}} - Object containing the resolved full file path and the resolved path (after alias resolution)
43
+ * @returns {{fullFilePath: string, resolvedPath: string, relativePath: string}} - Object containing the resolved full file path, resolved path (after alias resolution), and relative path
44
44
  */
45
45
  function resolveFilePathFromMatch(matchedFileString, syntax, configPath) {
46
46
  const relativePath = trimSurroundingQuotes(
@@ -36,6 +36,7 @@ function hasUnresolvedVars(str, variableSyntax) {
36
36
  * @param {object} context.config - Original config object
37
37
  * @param {string} context.configDir - Config file directory
38
38
  * @param {RegExp} context.variableSyntax - Variable syntax regex
39
+ * @param {object} [context.options] - CLI options
39
40
  * @returns {Promise<*>} Resolved value or undefined if can't pre-resolve
40
41
  */
41
42
  async function preResolveSingle(varString, context) {
@@ -43,8 +43,8 @@ function endChar(str, char) {
43
43
  /**
44
44
  * Ensures a value (string or array of strings) has quotes around it
45
45
  * @param {string|string[]} value - The value to quote
46
- * @param {string} open - Opening quote character (default: '"')
47
- * @param {string} close - Closing quote character (default: same as open)
46
+ * @param {string} [open] - Opening quote character (default: '"')
47
+ * @param {string} [close] - Closing quote character (default: same as open)
48
48
  * @returns {string|string[]} The quoted value(s)
49
49
  */
50
50
  function ensureQuote(value, open = '"', close) {
@@ -5,7 +5,7 @@ const { splitByComma } = require('./splitByComma')
5
5
  * NOTE: This is a simpler version that delegates to splitByComma for consistency.
6
6
  * For advanced use cases with bracket depth tracking and regex protection, use splitByComma directly.
7
7
  * @param {string} str - String to split
8
- * @param {string} splitter - Optional custom splitter (defaults to ',')
8
+ * @param {string} [splitter] - Optional custom splitter (defaults to ',')
9
9
  * @returns {string[]} Array of split strings
10
10
  */
11
11
  function splitCsv(str, splitter) {
@@ -2,10 +2,10 @@ const { makeHeader, logHeader : logHeaderBox } = require('@davidwells/box-logger
2
2
 
3
3
  function logHeader(message) {
4
4
  logHeaderBox({
5
- content: message,
6
- rightBorder: true,
7
- minWidth: 80,
8
- textStyle: 'bold',
5
+ content: message,
6
+ borderRight: true,
7
+ minWidth: 80,
8
+ fontStyle: 'bold',
9
9
  borderStyle: 'bold',
10
10
  borderColor: 'cyanBright',
11
11
  })
@@ -14,9 +14,9 @@ function isValidValue(val) {
14
14
  * Check if variable resolved to valid value, log warning if not
15
15
  * @param {string} variableString - The variable being resolved
16
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
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
20
  * @returns {*} The valueToPopulate unchanged
21
21
  */
22
22
  function warnIfNotFound(variableString, valueToPopulate, options = {}) {
@@ -338,10 +338,10 @@ function findNestedVariablesOld(input, regex, variablesKnownTypes, debug = false
338
338
 
339
339
  // Replace the `__REPLACED_${iteration - 1}__` with the original match
340
340
  matches = matches.map((match, index) => {
341
- const indexOfReplaced = match.varMatch.match(/__REPLACED_(\d+)__/)
341
+ const indexOfReplaced = match.fullMatch.match(/__REPLACED_(\d+)__/)
342
342
  if (indexOfReplaced) {
343
343
  const replacedIndex = parseInt(indexOfReplaced[1])
344
- match.varMatch = match.varMatch.replace(`__REPLACED_${replacedIndex}__`, matches[replacedIndex].variable)
344
+ match.fullMatch = match.fullMatch.replace(`__REPLACED_${replacedIndex}__`, matches[replacedIndex].variable)
345
345
  match.variable = match.variable.replace(`__REPLACED_${replacedIndex}__`, matches[replacedIndex].variable)
346
346
  }
347
347
  return match
@@ -1,3 +1,45 @@
1
+ /**
2
+ * Extract variable prefix/suffix from regex source
3
+ * @param {string} syntaxSource - The regex source string (e.g., '\\$\\{(...)\\}')
4
+ * @returns {{ prefix: string, suffix: string }} The unescaped prefix and suffix
5
+ */
6
+ function extractVariableWrapper(syntaxSource) {
7
+ // Find first capturing group ( that's not escaped and not a special group (?:, (?=, etc.)
8
+ let openParen = -1
9
+ for (let i = 0; i < syntaxSource.length; i++) {
10
+ if (syntaxSource[i] === '(' && (i === 0 || syntaxSource[i - 1] !== '\\')) {
11
+ // Check if it's a special group like (?:, (?=, (?!, (?<
12
+ if (syntaxSource[i + 1] !== '?') {
13
+ openParen = i
14
+ break
15
+ }
16
+ }
17
+ }
18
+
19
+ // Find last ) that's not escaped
20
+ let closeParen = -1
21
+ for (let i = syntaxSource.length - 1; i >= 0; i--) {
22
+ if (syntaxSource[i] === ')' && (i === 0 || syntaxSource[i - 1] !== '\\')) {
23
+ closeParen = i
24
+ break
25
+ }
26
+ }
27
+
28
+ let escapedPrefix = openParen > 0 ? syntaxSource.substring(0, openParen) : ''
29
+ const escapedSuffix = closeParen >= 0 ? syntaxSource.substring(closeParen + 1) : ''
30
+
31
+ // Strip any leading non-capturing groups like (?:...) from prefix
32
+ escapedPrefix = escapedPrefix.replace(/^\(\?:[^)]*\)/g, '')
33
+
34
+ // Unescape regex escapes: \$ -> $, \{ -> {, \[ -> [, etc.
35
+ const unescape = (s) => s.replace(/\\(.)/g, '$1')
36
+
37
+ return {
38
+ prefix: unescape(escapedPrefix) || '${',
39
+ suffix: unescape(escapedSuffix) || '}'
40
+ }
41
+ }
42
+
1
43
  /**
2
44
  * Get fallback variable string
3
45
  * @param {string[]} split - Array from split at comma
@@ -47,6 +89,7 @@ Remove or update the \${${variableString}} to fix
47
89
  }
48
90
 
49
91
  module.exports = {
92
+ extractVariableWrapper,
50
93
  getFallbackString,
51
94
  verifyVariable
52
95
  }
@@ -1,6 +1,6 @@
1
1
  const { test } = require('uvu')
2
2
  const assert = require('uvu/assert')
3
- const { getFallbackString, verifyVariable } = require('./variableUtils')
3
+ const { extractVariableWrapper, getFallbackString, verifyVariable } = require('./variableUtils')
4
4
 
5
5
  // Tests for getFallbackString
6
6
  test('getFallbackString - should reconstruct variable from split array', () => {
@@ -113,5 +113,42 @@ test('verifyVariable - should match with function and config param', () => {
113
113
  assert.is(result, true)
114
114
  })
115
115
 
116
+ // Tests for extractVariableWrapper
117
+ test('extractVariableWrapper - standard ${} syntax', () => {
118
+ const result = extractVariableWrapper('\\$\\{([^}]+)\\}')
119
+ assert.equal(result.prefix, '${')
120
+ assert.equal(result.suffix, '}')
121
+ })
122
+
123
+ test('extractVariableWrapper - double brace ${{}} syntax', () => {
124
+ const result = extractVariableWrapper('\\$\\{\\{([^}]+)\\}\\}')
125
+ assert.equal(result.prefix, '${{')
126
+ assert.equal(result.suffix, '}}')
127
+ })
128
+
129
+ test('extractVariableWrapper - hash #{} syntax', () => {
130
+ const result = extractVariableWrapper('\\#\\{([^}]+)\\}')
131
+ assert.equal(result.prefix, '#{')
132
+ assert.equal(result.suffix, '}')
133
+ })
134
+
135
+ test('extractVariableWrapper - angle bracket <> syntax', () => {
136
+ const result = extractVariableWrapper('\\<([^>]+)\\>')
137
+ assert.equal(result.prefix, '<')
138
+ assert.equal(result.suffix, '>')
139
+ })
140
+
141
+ test('extractVariableWrapper - double bracket [[]] syntax', () => {
142
+ const result = extractVariableWrapper('\\[\\[([^\\]]+)\\]\\]')
143
+ assert.equal(result.prefix, '[[')
144
+ assert.equal(result.suffix, ']]')
145
+ })
146
+
147
+ test('extractVariableWrapper - strips non-capturing group prefix', () => {
148
+ const result = extractVariableWrapper('(?:prefix)\\$\\{([^}]+)\\}')
149
+ assert.equal(result.prefix, '${')
150
+ assert.equal(result.suffix, '}')
151
+ })
152
+
116
153
  // Run all tests
117
154
  test.run()
package/types/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../cli.js"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ declare function _exports(string: string): string;
2
+ export = _exports;
3
+ //# sourceMappingURL=md5.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"md5.d.ts","sourceRoot":"","sources":["../../../src/functions/md5.js"],"names":[],"mappings":"AAOiB,kCAHL,MAAM,GACN,MAAM,CAOjB"}
@@ -0,0 +1,100 @@
1
+ declare namespace _exports {
2
+ export { ConfigoramaSettings, ConfigoramaResult };
3
+ }
4
+ declare function _exports<T = any>(configPathOrObject: string | any, settings?: ConfigoramaSettings): Promise<T | ConfigoramaResult<T>>;
5
+ declare namespace _exports {
6
+ export { Configorama };
7
+ export function sync<T = any>(configPathOrObject: string | any, settings?: ConfigoramaSettings): T;
8
+ export function analyze(configPathOrObject: string | object, settings?: object): Promise<any>;
9
+ export { parsers as format };
10
+ }
11
+ export = _exports;
12
+ type ConfigoramaSettings = {
13
+ /**
14
+ * - options to populate for ${opt:xyz}. These could be CLI flags
15
+ */
16
+ options?: {
17
+ [x: string]: any;
18
+ };
19
+ /**
20
+ * - Regex of variable syntax
21
+ */
22
+ syntax?: string;
23
+ /**
24
+ * - cwd of config. Needed if raw object passed in instead of file path
25
+ */
26
+ configDir?: string;
27
+ /**
28
+ * - array of custom variable sources
29
+ */
30
+ variableSources?: any[];
31
+ /**
32
+ * - Object of custom filters
33
+ */
34
+ filters?: {
35
+ [x: string]: Function;
36
+ };
37
+ /**
38
+ * - Object of custom functions
39
+ */
40
+ functions?: {
41
+ [x: string]: Function;
42
+ };
43
+ /**
44
+ * - allow unknown variables to pass through without throwing errors
45
+ */
46
+ allowUnknownVars?: boolean;
47
+ /**
48
+ * - allow undefined values to pass through without throwing errors
49
+ */
50
+ allowUndefinedValues?: boolean;
51
+ /**
52
+ * - values passed into .js config files if user using javascript config
53
+ */
54
+ dynamicArgs?: any | Function;
55
+ /**
56
+ * - return both config and metadata about variables found
57
+ */
58
+ returnMetadata?: boolean;
59
+ /**
60
+ * - keys to merge in arrays of objects
61
+ */
62
+ mergeKeys?: string[];
63
+ /**
64
+ * - map of file paths to override
65
+ */
66
+ filePathOverrides?: {
67
+ [x: string]: string;
68
+ };
69
+ };
70
+ type ConfigoramaResult<T = any> = {
71
+ /**
72
+ * - The variable syntax pattern used
73
+ */
74
+ variableSyntax: RegExp;
75
+ /**
76
+ * - Map of variable types found
77
+ */
78
+ variableTypes: {
79
+ [x: string]: any;
80
+ };
81
+ /**
82
+ * - The resolved configuration object
83
+ */
84
+ config: T;
85
+ /**
86
+ * - The original unresolved configuration
87
+ */
88
+ originalConfig: any;
89
+ /**
90
+ * - Metadata about variables found and resolved
91
+ */
92
+ metadata: any;
93
+ /**
94
+ * - Resolution history per path for debugging
95
+ */
96
+ resolutionHistory: any;
97
+ };
98
+ import Configorama = require("./main");
99
+ import parsers = require("./parsers");
100
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.js"],"names":[],"mappings":";;;AAwCiB,0BALH,CAAC,4BACJ,MAAM,MAAO,aACb,mBAAmB,GACjB,OAAO,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAiD7C;;;IASqB,qBALR,CAAC,4BACJ,MAAM,MAAO,aACb,mBAAmB,GACjB,CAAC,CAgBb;IAQwB,4CAJb,MAAM,GAAC,MAAM,aACd,MAAM,gBAUhB;;;;;;;;;;;;;;aApHa,MAAM;;;;gBACN,MAAM;;;;;;;;;;;;;;;;;;;;uBAIN,OAAO;;;;2BACP,OAAO;;;;kBACP,cAAe;;;;qBACf,OAAO;;;;gBACP,MAAM,EAAE;;;;;;;;uBAKR,CAAC;;;;oBAED,MAAM;;;;;;;;;;YAEN,CAAC"}