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,135 @@
1
+ const { test } = require('uvu')
2
+ const assert = require('uvu/assert')
3
+ const {
4
+ findOutermostBraces,
5
+ findOutermostBracesDepthFirst,
6
+ findOutermostVariables
7
+ } = require('./bracketMatcher')
8
+
9
+ // Tests for findOutermostBraces
10
+ test('findOutermostBraces - should find simple braces', () => {
11
+ const result = findOutermostBraces('text {content} more')
12
+ assert.equal(result, ['{content}'])
13
+ })
14
+
15
+ test('findOutermostBraces - should find multiple brace pairs', () => {
16
+ const result = findOutermostBraces('{first} and {second}')
17
+ assert.equal(result, ['{first}', '{second}'])
18
+ })
19
+
20
+ test('findOutermostBraces - should handle nested braces', () => {
21
+ const result = findOutermostBraces('{outer {inner} content}')
22
+ assert.equal(result, ['{outer {inner} content}'])
23
+ })
24
+
25
+ test('findOutermostBraces - should find with prefix', () => {
26
+ const result = findOutermostBraces('text ${variable} more', '{', '}', '$')
27
+ assert.equal(result, ['${variable}'])
28
+ })
29
+
30
+ test('findOutermostBraces - should handle multiple nested levels with prefix', () => {
31
+ const result = findOutermostBraces('${outer ${inner ${deepest}}}', '{', '}', '$')
32
+ assert.equal(result, ['${outer ${inner ${deepest}}}'])
33
+ })
34
+
35
+ test('findOutermostBraces - should return empty array when no matches', () => {
36
+ const result = findOutermostBraces('no braces here')
37
+ assert.equal(result, [])
38
+ })
39
+
40
+ test('findOutermostBraces - should handle custom delimiters', () => {
41
+ const result = findOutermostBraces('text [content] more', '[', ']')
42
+ assert.equal(result, ['[content]'])
43
+ })
44
+
45
+ test('findOutermostBraces - should not match without prefix when prefix is specified', () => {
46
+ const result = findOutermostBraces('{no prefix} ${with prefix}', '{', '}', '$')
47
+ assert.equal(result, ['${with prefix}'])
48
+ })
49
+
50
+ // Tests for findOutermostBracesDepthFirst
51
+ test('findOutermostBracesDepthFirst - should find simple braces', () => {
52
+ const result = findOutermostBracesDepthFirst('text {content} more')
53
+ assert.equal(result, ['{content}'])
54
+ })
55
+
56
+ test('findOutermostBracesDepthFirst - should find multiple brace pairs', () => {
57
+ const result = findOutermostBracesDepthFirst('{first} and {second}')
58
+ assert.equal(result, ['{first}', '{second}'])
59
+ })
60
+
61
+ test('findOutermostBracesDepthFirst - should handle nested braces', () => {
62
+ const result = findOutermostBracesDepthFirst('{outer {inner} content}')
63
+ assert.equal(result, ['{outer {inner} content}'])
64
+ })
65
+
66
+ test('findOutermostBracesDepthFirst - should return empty array when no matches', () => {
67
+ const result = findOutermostBracesDepthFirst('no braces here')
68
+ assert.equal(result, [])
69
+ })
70
+
71
+ test('findOutermostBracesDepthFirst - should handle custom delimiters', () => {
72
+ const result = findOutermostBracesDepthFirst('text [content] more', '[', ']')
73
+ assert.equal(result, ['[content]'])
74
+ })
75
+
76
+ test('findOutermostBracesDepthFirst - should handle unmatched braces gracefully', () => {
77
+ const result = findOutermostBracesDepthFirst('{opened but not closed')
78
+ assert.equal(result, [])
79
+ })
80
+
81
+ test('findOutermostBracesDepthFirst - should handle complex nested structures', () => {
82
+ const result = findOutermostBracesDepthFirst('{a {b {c}}} {d {e}}')
83
+ assert.equal(result, ['{a {b {c}}}', '{d {e}}'])
84
+ })
85
+
86
+ // Tests for findOutermostVariables
87
+ test('findOutermostVariables - should find simple variable', () => {
88
+ const result = findOutermostVariables('text ${variable} more')
89
+ assert.equal(result, ['${variable}'])
90
+ })
91
+
92
+ test('findOutermostVariables - should find multiple variables', () => {
93
+ const result = findOutermostVariables('${first} and ${second}')
94
+ assert.equal(result, ['${first}', '${second}'])
95
+ })
96
+
97
+ test('findOutermostVariables - should handle nested variables', () => {
98
+ const result = findOutermostVariables('${outer ${inner}}')
99
+ assert.equal(result, ['${outer ${inner}}'])
100
+ })
101
+
102
+ test('findOutermostVariables - should return empty array when no variables', () => {
103
+ const result = findOutermostVariables('no variables here')
104
+ assert.equal(result, [])
105
+ })
106
+
107
+ test('findOutermostVariables - should ignore plain braces without dollar sign', () => {
108
+ const result = findOutermostVariables('{not a variable} ${is a variable}')
109
+ assert.equal(result, ['${is a variable}'])
110
+ })
111
+
112
+ test('findOutermostVariables - should handle real-world serverless variables', () => {
113
+ const result = findOutermostVariables('${param:xyz}')
114
+ assert.equal(result, ['${param:xyz}'])
115
+ })
116
+
117
+ test('findOutermostVariables - should handle deeply nested serverless variables', () => {
118
+ const result = findOutermostVariables('${opt:stage, ${env:foo}}')
119
+ assert.equal(result, ['${opt:stage, ${env:foo}}'])
120
+ })
121
+
122
+ test('findOutermostVariables - should find multiple variables in array context', () => {
123
+ const text = "y: !Not [!Equals [!Join ['', ${param:xyz}]]]"
124
+ const result = findOutermostVariables(text)
125
+ assert.equal(result, ['${param:xyz}'])
126
+ })
127
+
128
+ test('findOutermostVariables - should handle variables in YAML object context', () => {
129
+ const text = 'key: { value: ${self:config}, other: ${env:var} }'
130
+ const result = findOutermostVariables(text)
131
+ assert.equal(result, ['${self:config}', '${env:var}'])
132
+ })
133
+
134
+ // Run all tests
135
+ test.run()
@@ -1,7 +1,8 @@
1
- const { trim } = require('./lodash')
1
+ const { trim } = require('../lodash')
2
+ const { trimSurroundingQuotes } = require('./quoteUtils')
2
3
 
3
4
  function formatArg(arg) {
4
- const cleanArg = trim(arg).replace(/^('|")/, '').replace(/('|")$/, '')
5
+ const cleanArg = trimSurroundingQuotes(trim(arg), false)
5
6
  if (cleanArg.match(/^{([^}]+)}$/)) {
6
7
  return JSON.parse(cleanArg)
7
8
  }
@@ -0,0 +1,77 @@
1
+ const { test } = require('uvu')
2
+ const assert = require('uvu/assert')
3
+ const formatFunctionArgs = require('./formatFunctionArgs')
4
+
5
+ test('formatFunctionArgs - should handle single string argument', () => {
6
+ const result = formatFunctionArgs('simpleString')
7
+ assert.equal(result, 'simpleString')
8
+ })
9
+
10
+ test('formatFunctionArgs - should trim and remove quotes from string', () => {
11
+ const result = formatFunctionArgs(' "quoted" ')
12
+ assert.equal(result, 'quoted')
13
+ })
14
+
15
+ test('formatFunctionArgs - should parse JSON object', () => {
16
+ const result = formatFunctionArgs('{"key":"value"}')
17
+ assert.is(typeof result, 'object')
18
+ assert.equal(result.key, 'value')
19
+ })
20
+
21
+ test('formatFunctionArgs - should parse JSON array', () => {
22
+ const result = formatFunctionArgs('[1,2,3]')
23
+ assert.is(Array.isArray(result), true)
24
+ assert.equal(result, [1, 2, 3])
25
+ })
26
+
27
+ test('formatFunctionArgs - should handle array of arguments', () => {
28
+ const result = formatFunctionArgs(['arg1', '"arg2"', ' arg3 '])
29
+ assert.is(Array.isArray(result), true)
30
+ assert.equal(result, ['arg1', 'arg2', 'arg3'])
31
+ })
32
+
33
+ test('formatFunctionArgs - should parse JSON objects in array', () => {
34
+ const result = formatFunctionArgs(['{"x":1}', '{"y":2}'])
35
+ assert.is(Array.isArray(result), true)
36
+ assert.equal(result[0].x, 1)
37
+ assert.equal(result[1].y, 2)
38
+ })
39
+
40
+ test('formatFunctionArgs - should parse JSON arrays in array', () => {
41
+ const result = formatFunctionArgs(['[1,2]', '[3,4]'])
42
+ assert.is(Array.isArray(result), true)
43
+ assert.equal(result[0], [1, 2])
44
+ assert.equal(result[1], [3, 4])
45
+ })
46
+
47
+ test('formatFunctionArgs - should handle mixed array', () => {
48
+ const result = formatFunctionArgs(['simple', '{"obj":"val"}', '[1,2]'])
49
+ assert.equal(result[0], 'simple')
50
+ assert.equal(result[1].obj, 'val')
51
+ assert.equal(result[2], [1, 2])
52
+ })
53
+
54
+ test('formatFunctionArgs - should handle single quotes', () => {
55
+ const result = formatFunctionArgs("'singleQuoted'")
56
+ assert.equal(result, 'singleQuoted')
57
+ })
58
+
59
+ test('formatFunctionArgs - should handle empty object', () => {
60
+ const result = formatFunctionArgs('{}')
61
+ assert.is(typeof result, 'object')
62
+ assert.equal(Object.keys(result).length, 0)
63
+ })
64
+
65
+ test('formatFunctionArgs - should handle empty array', () => {
66
+ const result = formatFunctionArgs('[]')
67
+ // Empty array regex pattern doesn't match /^\[([^}]+)\]$/ so returns the string as-is
68
+ assert.equal(result, '[]')
69
+ })
70
+
71
+ test('formatFunctionArgs - should preserve unquoted strings', () => {
72
+ const result = formatFunctionArgs('unquoted')
73
+ assert.equal(result, 'unquoted')
74
+ })
75
+
76
+ // Run all tests
77
+ test.run()
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Quote manipulation utilities for string handling
3
+ */
4
+
5
+ /**
6
+ * Removes surrounding quotes (single, double, or backtick) from a string
7
+ * @param {string} str - The string to trim
8
+ * @param {boolean} includeBackticks - Whether to also trim backticks (default: true)
9
+ * @returns {string} The trimmed string
10
+ */
11
+ function trimSurroundingQuotes(str = '', includeBackticks = true) {
12
+ let result = str
13
+ .replace(/^(")([^"\n]*?)(\1)$/, "$2")
14
+ .replace(/^(')([^'\n]*?)(\1)$/, "$2")
15
+
16
+ if (includeBackticks) {
17
+ result = result.replace(/^(`)([^`\n]*?)(\1)$/, "$2")
18
+ }
19
+
20
+ return result
21
+ }
22
+
23
+ /**
24
+ * Checks if a string starts with the given character
25
+ * @param {string} str - The string to check
26
+ * @param {string} char - The character to check for
27
+ * @returns {string} Empty string if starts with char, otherwise the char
28
+ */
29
+ function startChar(str, char) {
30
+ return (str[0] === char) ? '' : char
31
+ }
32
+
33
+ /**
34
+ * Checks if a string ends with the given character
35
+ * @param {string} str - The string to check
36
+ * @param {string} char - The character to check for
37
+ * @returns {string} Empty string if ends with char, otherwise the char
38
+ */
39
+ function endChar(str, char) {
40
+ return (str[str.length - 1] === char) ? '' : char
41
+ }
42
+
43
+ /**
44
+ * Ensures a value (string or array of strings) has quotes around it
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)
48
+ * @returns {string|string[]} The quoted value(s)
49
+ */
50
+ function ensureQuote(value, open = '"', close) {
51
+ let i = -1
52
+ const result = []
53
+ const end = close || open
54
+ if (typeof value === 'string') {
55
+ return startChar(value, open) + value + endChar(value, end)
56
+ }
57
+ while (++i < value.length) {
58
+ result[i] = startChar(value[i], open) + value[i] + endChar(value[i], end)
59
+ }
60
+ return result
61
+ }
62
+
63
+ /**
64
+ * Checks if a string is surrounded by matching quotes (single or double)
65
+ * @param {string} str - The string to check
66
+ * @returns {boolean} True if surrounded by matching quotes
67
+ */
68
+ function isSurroundedByQuotes(str) {
69
+ if (!str || str.length < 2) return false
70
+ const firstChar = str[0]
71
+ const lastChar = str[str.length - 1]
72
+ return (firstChar === "'" && lastChar === "'") || (firstChar === '"' && lastChar === '"')
73
+ }
74
+
75
+ /**
76
+ * Checks if a string starts with a quoted value followed by a pipe
77
+ * @param {string} str - The string to check
78
+ * @returns {boolean} True if matches pattern like 'xyz' | or "xyz" |
79
+ */
80
+ function startsWithQuotedPipe(str) {
81
+ return /^(['"])(.*?)\1\s*\|/.test(str)
82
+ }
83
+
84
+ module.exports = {
85
+ trimSurroundingQuotes,
86
+ ensureQuote,
87
+ isSurroundedByQuotes,
88
+ startsWithQuotedPipe
89
+ }
@@ -0,0 +1,217 @@
1
+ const { test } = require('uvu')
2
+ const assert = require('uvu/assert')
3
+ const { trimSurroundingQuotes: trimQuotes, ensureQuote, isSurroundedByQuotes, startsWithQuotedPipe } = require('./quoteUtils')
4
+
5
+ // Tests for double quotes
6
+ test('trimQuotes - should remove surrounding double quotes', () => {
7
+ const result = trimQuotes('"hello"')
8
+ assert.equal(result, 'hello')
9
+ })
10
+
11
+ test('trimQuotes - should not remove non-surrounding double quotes', () => {
12
+ const result = trimQuotes('say "hello" there')
13
+ assert.equal(result, 'say "hello" there')
14
+ })
15
+
16
+ test('trimQuotes - should preserve internal double quotes', () => {
17
+ const result = trimQuotes('"has "nested" quotes"')
18
+ assert.equal(result, '"has "nested" quotes"')
19
+ })
20
+
21
+ // Tests for single quotes
22
+ test('trimQuotes - should remove surrounding single quotes', () => {
23
+ const result = trimQuotes("'hello'")
24
+ assert.equal(result, 'hello')
25
+ })
26
+
27
+ test('trimQuotes - should not remove non-surrounding single quotes', () => {
28
+ const result = trimQuotes("say 'hello' there")
29
+ assert.equal(result, "say 'hello' there")
30
+ })
31
+
32
+ test('trimQuotes - should preserve internal single quotes', () => {
33
+ const result = trimQuotes("'has 'nested' quotes'")
34
+ assert.equal(result, "'has 'nested' quotes'")
35
+ })
36
+
37
+ // Tests for backticks
38
+ test('trimQuotes - should remove surrounding backticks by default', () => {
39
+ const result = trimQuotes('`hello`')
40
+ assert.equal(result, 'hello')
41
+ })
42
+
43
+ test('trimQuotes - should not remove backticks when includeBackticks is false', () => {
44
+ const result = trimQuotes('`hello`', false)
45
+ assert.equal(result, '`hello`')
46
+ })
47
+
48
+ test('trimQuotes - should preserve internal backticks', () => {
49
+ const result = trimQuotes('`has `nested` ticks`')
50
+ assert.equal(result, '`has `nested` ticks`')
51
+ })
52
+
53
+ // Tests for mixed quotes
54
+ test('trimQuotes - should not remove mismatched quotes', () => {
55
+ const result = trimQuotes('"hello\'')
56
+ assert.equal(result, '"hello\'')
57
+ })
58
+
59
+ test('trimQuotes - should handle empty quotes', () => {
60
+ const result = trimQuotes('""')
61
+ assert.equal(result, '')
62
+ })
63
+
64
+ test('trimQuotes - should handle empty single quotes', () => {
65
+ const result = trimQuotes("''")
66
+ assert.equal(result, '')
67
+ })
68
+
69
+ test('trimQuotes - should handle empty backticks', () => {
70
+ const result = trimQuotes('``')
71
+ assert.equal(result, '')
72
+ })
73
+
74
+ // Tests for edge cases
75
+ test('trimQuotes - should handle empty string', () => {
76
+ const result = trimQuotes('')
77
+ assert.equal(result, '')
78
+ })
79
+
80
+ test('trimQuotes - should handle undefined', () => {
81
+ const result = trimQuotes()
82
+ assert.equal(result, '')
83
+ })
84
+
85
+ test('trimQuotes - should handle strings without quotes', () => {
86
+ const result = trimQuotes('hello world')
87
+ assert.equal(result, 'hello world')
88
+ })
89
+
90
+ test('trimQuotes - should handle strings with only opening quote', () => {
91
+ const result = trimQuotes('"hello')
92
+ assert.equal(result, '"hello')
93
+ })
94
+
95
+ test('trimQuotes - should handle strings with only closing quote', () => {
96
+ const result = trimQuotes('hello"')
97
+ assert.equal(result, 'hello"')
98
+ })
99
+
100
+ test('trimQuotes - should not remove quotes from multi-line strings', () => {
101
+ const result = trimQuotes('"hello\nworld"')
102
+ assert.equal(result, '"hello\nworld"')
103
+ })
104
+
105
+ test('trimQuotes - should handle spaces inside quotes', () => {
106
+ const result = trimQuotes('" hello "')
107
+ assert.equal(result, ' hello ')
108
+ })
109
+
110
+ test('trimQuotes - should handle special characters inside quotes', () => {
111
+ const result = trimQuotes('"hello@#$%world"')
112
+ assert.equal(result, 'hello@#$%world')
113
+ })
114
+
115
+ test('trimQuotes - should handle numbers as strings', () => {
116
+ const result = trimQuotes('"12345"')
117
+ assert.equal(result, '12345')
118
+ })
119
+
120
+ test('trimQuotes - should handle JSON-like strings', () => {
121
+ const result = trimQuotes('"{key: value}"')
122
+ assert.equal(result, '{key: value}')
123
+ })
124
+
125
+ // Tests for backtick parameter
126
+ test('trimQuotes - should process all quote types when includeBackticks is true', () => {
127
+ assert.equal(trimQuotes('"test"', true), 'test')
128
+ assert.equal(trimQuotes("'test'", true), 'test')
129
+ assert.equal(trimQuotes('`test`', true), 'test')
130
+ })
131
+
132
+ test('trimQuotes - should not process backticks when includeBackticks is false', () => {
133
+ assert.equal(trimQuotes('"test"', false), 'test')
134
+ assert.equal(trimQuotes("'test'", false), 'test')
135
+ assert.equal(trimQuotes('`test`', false), '`test`')
136
+ })
137
+
138
+ // Tests for ensureQuote
139
+ test('ensureQuote - should add double quotes to unquoted string', () => {
140
+ assert.equal(ensureQuote('hello'), '"hello"')
141
+ })
142
+
143
+ test('ensureQuote - should not double-quote already quoted string', () => {
144
+ assert.equal(ensureQuote('"hello"'), '"hello"')
145
+ })
146
+
147
+ test('ensureQuote - should use custom open/close characters', () => {
148
+ assert.equal(ensureQuote('hello', "'"), "'hello'")
149
+ })
150
+
151
+ test('ensureQuote - should handle different open and close characters', () => {
152
+ assert.equal(ensureQuote('hello', '[', ']'), '[hello]')
153
+ })
154
+
155
+ test('ensureQuote - should handle array of strings', () => {
156
+ const result = ensureQuote(['a', 'b', 'c'])
157
+ assert.equal(result, ['"a"', '"b"', '"c"'])
158
+ })
159
+
160
+ test('ensureQuote - should not re-quote already quoted items in array', () => {
161
+ const result = ensureQuote(['"a"', 'b'])
162
+ assert.equal(result, ['"a"', '"b"'])
163
+ })
164
+
165
+ // Tests for isSurroundedByQuotes
166
+ test('isSurroundedByQuotes - should return true for double-quoted string', () => {
167
+ assert.equal(isSurroundedByQuotes('"hello"'), true)
168
+ })
169
+
170
+ test('isSurroundedByQuotes - should return true for single-quoted string', () => {
171
+ assert.equal(isSurroundedByQuotes("'hello'"), true)
172
+ })
173
+
174
+ test('isSurroundedByQuotes - should return false for unquoted string', () => {
175
+ assert.equal(isSurroundedByQuotes('hello'), false)
176
+ })
177
+
178
+ test('isSurroundedByQuotes - should return false for mismatched quotes', () => {
179
+ assert.equal(isSurroundedByQuotes('"hello\''), false)
180
+ })
181
+
182
+ test('isSurroundedByQuotes - should return false for empty string', () => {
183
+ assert.equal(isSurroundedByQuotes(''), false)
184
+ })
185
+
186
+ test('isSurroundedByQuotes - should return false for null/undefined', () => {
187
+ assert.equal(isSurroundedByQuotes(null), false)
188
+ assert.equal(isSurroundedByQuotes(undefined), false)
189
+ })
190
+
191
+ test('isSurroundedByQuotes - should return false for single character', () => {
192
+ assert.equal(isSurroundedByQuotes('"'), false)
193
+ })
194
+
195
+ // Tests for startsWithQuotedPipe
196
+ test('startsWithQuotedPipe - should match single-quoted value with pipe', () => {
197
+ assert.equal(startsWithQuotedPipe("'value' | filter"), true)
198
+ })
199
+
200
+ test('startsWithQuotedPipe - should match double-quoted value with pipe', () => {
201
+ assert.equal(startsWithQuotedPipe('"value" | filter'), true)
202
+ })
203
+
204
+ test('startsWithQuotedPipe - should not match unquoted value with pipe', () => {
205
+ assert.equal(startsWithQuotedPipe('value | filter'), false)
206
+ })
207
+
208
+ test('startsWithQuotedPipe - should not match quoted value without pipe', () => {
209
+ assert.equal(startsWithQuotedPipe('"value"'), false)
210
+ })
211
+
212
+ test('startsWithQuotedPipe - should handle spaces around pipe', () => {
213
+ assert.equal(startsWithQuotedPipe("'xyz' | something"), true)
214
+ })
215
+
216
+ // Run all tests
217
+ test.run()
@@ -0,0 +1,82 @@
1
+ const { test } = require('uvu')
2
+ const assert = require('uvu/assert')
3
+ const { replaceAll } = require('./replaceAll')
4
+
5
+ test('replaceAll - should replace simple string', () => {
6
+ const result = replaceAll('foo', 'bar', 'foo is foo')
7
+ assert.equal(result, 'bar is bar')
8
+ })
9
+
10
+ test('replaceAll - should replace all occurrences', () => {
11
+ const result = replaceAll('a', 'x', 'aaa bbb aaa')
12
+ assert.equal(result, 'xxx bbb xxx')
13
+ })
14
+
15
+ test('replaceAll - should handle regex special characters in search string', () => {
16
+ const result = replaceAll('.', 'X', 'a.b.c')
17
+ assert.equal(result, 'aXbXc')
18
+ })
19
+
20
+ test('replaceAll - should handle $ in replacement string', () => {
21
+ const result = replaceAll('foo', '$bar', 'foo is foo')
22
+ assert.equal(result, '$bar is $bar')
23
+ })
24
+
25
+ test('replaceAll - should handle parentheses in search string', () => {
26
+ const result = replaceAll('(test)', 'result', 'this (test) is a (test)')
27
+ assert.equal(result, 'this result is a result')
28
+ })
29
+
30
+ test('replaceAll - should handle square brackets in search string', () => {
31
+ const result = replaceAll('[item]', 'value', 'get [item] and [item]')
32
+ assert.equal(result, 'get value and value')
33
+ })
34
+
35
+ test('replaceAll - should handle forward slashes', () => {
36
+ const result = replaceAll('/', '-', 'path/to/file')
37
+ assert.equal(result, 'path-to-file')
38
+ })
39
+
40
+ test('replaceAll - should handle backslashes', () => {
41
+ const result = replaceAll('\\', '/', 'path\\to\\file')
42
+ assert.equal(result, 'path/to/file')
43
+ })
44
+
45
+ test('replaceAll - should handle curly braces', () => {
46
+ const result = replaceAll('{var}', 'value', 'get {var} and {var}')
47
+ assert.equal(result, 'get value and value')
48
+ })
49
+
50
+ test('replaceAll - should handle asterisks', () => {
51
+ const result = replaceAll('*', 'star', '* is *')
52
+ assert.equal(result, 'star is star')
53
+ })
54
+
55
+ test('replaceAll - should handle plus signs', () => {
56
+ const result = replaceAll('+', 'plus', 'a+b+c')
57
+ console.log('result', result)
58
+ assert.equal(result, 'aplusbplusc')
59
+ })
60
+
61
+ test('replaceAll - should handle question marks', () => {
62
+ const result = replaceAll('?', 'Q', 'what? why?')
63
+ assert.equal(result, 'whatQ whyQ')
64
+ })
65
+
66
+ test('replaceAll - should handle empty replacement', () => {
67
+ const result = replaceAll('foo', '', 'foo bar foo')
68
+ assert.equal(result, ' bar ')
69
+ })
70
+
71
+ test('replaceAll - should handle no matches', () => {
72
+ const result = replaceAll('xyz', 'abc', 'hello world')
73
+ assert.equal(result, 'hello world')
74
+ })
75
+
76
+ test('replaceAll - should handle complex regex special characters', () => {
77
+ const result = replaceAll('${var}', 'value', 'get ${var} and ${var}')
78
+ assert.equal(result, 'get value and value')
79
+ })
80
+
81
+ // Run all tests
82
+ test.run()
@@ -1,4 +1,4 @@
1
- const stringResolver = require('../resolvers/valueFromString')
1
+ const stringResolver = require('../../resolvers/valueFromString')
2
2
  const overwriteSyntax = RegExp(/\s*(?:,\s*)+/g) // /\s*(?:,\s*)+/g
3
3
  const stringRefSyntax = stringResolver.match
4
4
  /**
@@ -0,0 +1,38 @@
1
+ const { splitByComma } = require('./splitByComma')
2
+
3
+ /**
4
+ * Split a string by comma while preserving quoted content
5
+ * NOTE: This is a simpler version that delegates to splitByComma for consistency.
6
+ * For advanced use cases with bracket depth tracking and regex protection, use splitByComma directly.
7
+ * @param {string} str - String to split
8
+ * @param {string} splitter - Optional custom splitter (defaults to ',')
9
+ * @returns {string[]} Array of split strings
10
+ */
11
+ function splitCsv(str, splitter) {
12
+ // If custom splitter is provided, fall back to original simple implementation
13
+ if (splitter && splitter !== ',') {
14
+ const splitSyntax = splitter
15
+ return str.split(splitSyntax).reduce(
16
+ (acc, curr) => {
17
+ if (acc.isConcatting) {
18
+ acc.soFar[acc.soFar.length - 1] += splitter + curr
19
+ } else {
20
+ acc.soFar.push(curr)
21
+ }
22
+ if (curr.split('"').length % 2 == 0) {
23
+ acc.isConcatting = !acc.isConcatting
24
+ }
25
+ return acc
26
+ },
27
+ {
28
+ soFar: [],
29
+ isConcatting: false,
30
+ },
31
+ ).soFar
32
+ }
33
+
34
+ // For standard comma splitting, use the more robust splitByComma
35
+ return splitByComma(str)
36
+ }
37
+
38
+ module.exports = { splitCsv }