configorama 0.9.8 → 0.9.12
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 +83 -0
- package/index.d.ts +38 -29
- package/package.json +1 -1
- package/src/main.js +254 -101
- package/src/parsers/esm.js +0 -14
- package/src/parsers/typescript.js +0 -10
- package/src/resolvers/valueFromEval.js +69 -11
- package/src/resolvers/valueFromFile.js +1 -1
- package/src/resolvers/valueFromIf.js +75 -0
- package/src/resolvers/valueFromIf.test.js +66 -0
- package/src/resolvers/valueFromNumber.js +3 -0
- package/src/utils/handleSignalEvents.js +3 -4
- package/src/utils/lodash.js +18 -7
- package/src/utils/parsing/cloudformationSchema.js +1 -2
- package/src/utils/parsing/cloudformationSchema.test.js +14 -0
- package/src/utils/parsing/preProcess.js +220 -5
- package/src/utils/paths/getFullFilePath.js +6 -2
- package/src/utils/paths/getFullFilePath.test.js +18 -0
- package/src/utils/regex/index.js +18 -3
- package/src/utils/regex/index.test.js +24 -0
- package/src/utils/strings/quoteAware.js +141 -0
- package/src/utils/strings/replaceAll.js +13 -1
- package/src/utils/strings/splitByComma.js +25 -15
- package/src/utils/strings/splitByComma.test.js +19 -0
- package/src/utils/strings/splitOnPipe.js +30 -0
- package/src/utils/strings/splitOnPipe.test.js +68 -0
- package/src/utils/validation/isValidValue.test.js +1 -1
- package/src/utils/variables/findNestedVariables.js +8 -2
- package/types/src/main.d.ts +3 -1
- package/types/src/main.d.ts.map +1 -1
- package/types/src/parsers/esm.d.ts.map +1 -1
- package/types/src/parsers/typescript.d.ts.map +1 -1
- package/types/src/resolvers/valueFromEval.d.ts +1 -0
- package/types/src/resolvers/valueFromEval.d.ts.map +1 -1
- package/types/src/resolvers/valueFromIf.d.ts +7 -0
- package/types/src/resolvers/valueFromIf.d.ts.map +1 -0
- package/types/src/resolvers/valueFromNumber.d.ts.map +1 -1
- package/types/src/utils/handleSignalEvents.d.ts.map +1 -1
- package/types/src/utils/lodash.d.ts.map +1 -1
- package/types/src/utils/parsing/preProcess.d.ts +5 -1
- package/types/src/utils/parsing/preProcess.d.ts.map +1 -1
- package/types/src/utils/paths/getFullFilePath.d.ts.map +1 -1
- package/types/src/utils/regex/index.d.ts.map +1 -1
- package/types/src/utils/strings/quoteAware.d.ts +30 -0
- package/types/src/utils/strings/quoteAware.d.ts.map +1 -0
- package/types/src/utils/strings/replaceAll.d.ts.map +1 -1
- package/types/src/utils/strings/splitByComma.d.ts +1 -1
- package/types/src/utils/strings/splitByComma.d.ts.map +1 -1
- package/types/src/utils/strings/splitOnPipe.d.ts +8 -0
- package/types/src/utils/strings/splitOnPipe.d.ts.map +1 -0
- package/types/src/utils/variables/findNestedVariables.d.ts.map +1 -1
package/src/utils/regex/index.js
CHANGED
|
@@ -27,12 +27,27 @@ function parseFunctionCall(str) {
|
|
|
27
27
|
|
|
28
28
|
let depth = 1
|
|
29
29
|
let pos = startPos
|
|
30
|
-
|
|
30
|
+
let inString = null // null, '"', or "'"
|
|
31
|
+
|
|
31
32
|
// Track parenthesis depth to find matching closing paren
|
|
32
33
|
while (pos < str.length && depth > 0) {
|
|
33
34
|
const char = str[pos]
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
const prevChar = pos > 0 ? str[pos - 1] : ''
|
|
36
|
+
|
|
37
|
+
// Toggle string state on unescaped quotes
|
|
38
|
+
if ((char === '"' || char === "'") && prevChar !== '\\') {
|
|
39
|
+
if (!inString) {
|
|
40
|
+
inString = char
|
|
41
|
+
} else if (char === inString) {
|
|
42
|
+
inString = null
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Only count parens outside strings
|
|
47
|
+
if (!inString) {
|
|
48
|
+
if (char === '(') depth++
|
|
49
|
+
else if (char === ')') depth--
|
|
50
|
+
}
|
|
36
51
|
pos++
|
|
37
52
|
}
|
|
38
53
|
|
|
@@ -91,6 +91,30 @@ test('parseFunctionCall - handles multiple parens in text', () => {
|
|
|
91
91
|
assert.is(result[2], "'Choose option (A) or (B) or (C)'")
|
|
92
92
|
})
|
|
93
93
|
|
|
94
|
+
test('parseFunctionCall - handles unbalanced close paren in string', () => {
|
|
95
|
+
const result = parseFunctionCall('func("value)")')
|
|
96
|
+
assert.ok(result)
|
|
97
|
+
assert.is(result[0], 'func("value)")')
|
|
98
|
+
assert.is(result[1], 'func')
|
|
99
|
+
assert.is(result[2], '"value)"')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('parseFunctionCall - handles unbalanced open paren in string', () => {
|
|
103
|
+
const result = parseFunctionCall("func('open (')")
|
|
104
|
+
assert.ok(result)
|
|
105
|
+
assert.is(result[0], "func('open (')")
|
|
106
|
+
assert.is(result[1], 'func')
|
|
107
|
+
assert.is(result[2], "'open ('")
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('parseFunctionCall - handles multiple unbalanced parens in string', () => {
|
|
111
|
+
const result = parseFunctionCall('func("))))")')
|
|
112
|
+
assert.ok(result)
|
|
113
|
+
assert.is(result[0], 'func("))))")')
|
|
114
|
+
assert.is(result[1], 'func')
|
|
115
|
+
assert.is(result[2], '"))))"')
|
|
116
|
+
})
|
|
117
|
+
|
|
94
118
|
// ==========================================
|
|
95
119
|
// parseFunctionCall - edge cases
|
|
96
120
|
// ==========================================
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/* Quote-aware string processing utilities */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Find index of a character/pattern outside of quoted strings
|
|
5
|
+
* @param {string} str - String to search
|
|
6
|
+
* @param {string|function} matcher - Char to find, or function(str, idx) => matchLength|0
|
|
7
|
+
* @param {number} [startIdx=0] - Start index
|
|
8
|
+
* @returns {number} Index of match, or -1 if not found
|
|
9
|
+
*/
|
|
10
|
+
function findOutsideQuotes(str, matcher, startIdx = 0) {
|
|
11
|
+
let inQuote = false
|
|
12
|
+
let quoteChar = ''
|
|
13
|
+
|
|
14
|
+
for (let i = startIdx; i < str.length; i++) {
|
|
15
|
+
const ch = str[i]
|
|
16
|
+
|
|
17
|
+
if (!inQuote && (ch === '"' || ch === "'")) {
|
|
18
|
+
inQuote = true
|
|
19
|
+
quoteChar = ch
|
|
20
|
+
} else if (inQuote && ch === quoteChar) {
|
|
21
|
+
inQuote = false
|
|
22
|
+
} else if (!inQuote) {
|
|
23
|
+
if (typeof matcher === 'function') {
|
|
24
|
+
const matchLen = matcher(str, i)
|
|
25
|
+
if (matchLen > 0) return i
|
|
26
|
+
} else if (ch === matcher) {
|
|
27
|
+
return i
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return -1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Replace a pattern only outside of quoted strings
|
|
37
|
+
* @param {string} str - String to process
|
|
38
|
+
* @param {string|RegExp} pattern - Pattern to match (if string, must be exact match)
|
|
39
|
+
* @param {string|function} replacement - Replacement string or function(match) => string
|
|
40
|
+
* @returns {string} Processed string
|
|
41
|
+
*/
|
|
42
|
+
function replaceOutsideQuotes(str, pattern, replacement) {
|
|
43
|
+
let result = ''
|
|
44
|
+
let inQuote = false
|
|
45
|
+
let quoteChar = ''
|
|
46
|
+
let i = 0
|
|
47
|
+
|
|
48
|
+
const patternStr = typeof pattern === 'string' ? pattern : null
|
|
49
|
+
const patternLen = patternStr ? patternStr.length : 0
|
|
50
|
+
|
|
51
|
+
while (i < str.length) {
|
|
52
|
+
const ch = str[i]
|
|
53
|
+
|
|
54
|
+
if (!inQuote && (ch === '"' || ch === "'")) {
|
|
55
|
+
inQuote = true
|
|
56
|
+
quoteChar = ch
|
|
57
|
+
result += ch
|
|
58
|
+
i++
|
|
59
|
+
} else if (inQuote && ch === quoteChar) {
|
|
60
|
+
inQuote = false
|
|
61
|
+
result += ch
|
|
62
|
+
i++
|
|
63
|
+
} else if (!inQuote && patternStr) {
|
|
64
|
+
// String pattern - check for exact match with word boundaries
|
|
65
|
+
if (str.substring(i, i + patternLen) === patternStr) {
|
|
66
|
+
const before = i === 0 || !/\w/.test(str[i - 1])
|
|
67
|
+
const after = i + patternLen >= str.length || !/\w/.test(str[i + patternLen])
|
|
68
|
+
if (before && after) {
|
|
69
|
+
const rep = typeof replacement === 'function' ? replacement(patternStr) : replacement
|
|
70
|
+
result += rep
|
|
71
|
+
i += patternLen
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
result += ch
|
|
76
|
+
i++
|
|
77
|
+
} else {
|
|
78
|
+
result += ch
|
|
79
|
+
i++
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return result
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if an index is inside a quoted string
|
|
88
|
+
* @param {string} str - String to check
|
|
89
|
+
* @param {number} idx - Index to check
|
|
90
|
+
* @returns {boolean} True if index is inside quotes
|
|
91
|
+
*/
|
|
92
|
+
function isInsideQuotes(str, idx) {
|
|
93
|
+
let inQuote = false
|
|
94
|
+
let quoteChar = ''
|
|
95
|
+
|
|
96
|
+
for (let i = 0; i < str.length && i <= idx; i++) {
|
|
97
|
+
const ch = str[i]
|
|
98
|
+
if (!inQuote && (ch === '"' || ch === "'")) {
|
|
99
|
+
inQuote = true
|
|
100
|
+
quoteChar = ch
|
|
101
|
+
} else if (inQuote && ch === quoteChar) {
|
|
102
|
+
inQuote = false
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return inQuote
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get ranges of quoted strings in a string
|
|
111
|
+
* @param {string} str - String to analyze
|
|
112
|
+
* @returns {Array<[number, number]>} Array of [start, end] ranges
|
|
113
|
+
*/
|
|
114
|
+
function getQuoteRanges(str) {
|
|
115
|
+
/** @type {Array<[number, number]>} */
|
|
116
|
+
const ranges = []
|
|
117
|
+
let inQuote = false
|
|
118
|
+
let quoteChar = ''
|
|
119
|
+
let quoteStart = 0
|
|
120
|
+
|
|
121
|
+
for (let i = 0; i < str.length; i++) {
|
|
122
|
+
const ch = str[i]
|
|
123
|
+
if (!inQuote && (ch === '"' || ch === "'")) {
|
|
124
|
+
inQuote = true
|
|
125
|
+
quoteChar = ch
|
|
126
|
+
quoteStart = i
|
|
127
|
+
} else if (inQuote && ch === quoteChar) {
|
|
128
|
+
ranges.push([quoteStart, i + 1])
|
|
129
|
+
inQuote = false
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return ranges
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = {
|
|
137
|
+
findOutsideQuotes,
|
|
138
|
+
replaceOutsideQuotes,
|
|
139
|
+
isInsideQuotes,
|
|
140
|
+
getQuoteRanges
|
|
141
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const REPLACE_PATTERN = /([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|<>\-\&])/g
|
|
2
2
|
|
|
3
|
+
// Cache for compiled regex patterns (perf: avoid recompilation)
|
|
4
|
+
const regexCache = new Map()
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* Replace all occurrences of a string while handling regex special characters
|
|
5
8
|
* @param {string} replaceThis - String to replace
|
|
@@ -9,7 +12,16 @@ const REPLACE_PATTERN = /([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|<>\-\&])/g
|
|
|
9
12
|
*/
|
|
10
13
|
function replaceAll(replaceThis, withThis, inThis) {
|
|
11
14
|
withThis = withThis.replace(/\$/g, '$$$$')
|
|
12
|
-
|
|
15
|
+
|
|
16
|
+
// Check cache first
|
|
17
|
+
let pat = regexCache.get(replaceThis)
|
|
18
|
+
if (!pat) {
|
|
19
|
+
pat = new RegExp(replaceThis.replace(REPLACE_PATTERN, '\\$&'), 'g')
|
|
20
|
+
regexCache.set(replaceThis, pat)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Reset lastIndex for global regex reuse
|
|
24
|
+
pat.lastIndex = 0
|
|
13
25
|
return inThis.replace(pat, withThis)
|
|
14
26
|
}
|
|
15
27
|
|
|
@@ -32,7 +32,7 @@ function splitByComma(string, regexPattern) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const result = []
|
|
35
|
-
let
|
|
35
|
+
let segmentStart = 0 // Track segment start index (perf: avoid string concat)
|
|
36
36
|
let inQuote = false
|
|
37
37
|
let quoteChar = ""
|
|
38
38
|
let bracketDepth = 0 // Includes (), [], and {}
|
|
@@ -43,12 +43,22 @@ function splitByComma(string, regexPattern) {
|
|
|
43
43
|
const prevChar = i > 0 ? protectedString[i-1] : ''
|
|
44
44
|
|
|
45
45
|
// Handle quotes
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
if (char === "'" || char === '"') {
|
|
47
|
+
// Count consecutive backslashes before this quote
|
|
48
|
+
let backslashCount = 0
|
|
49
|
+
for (let j = i - 1; j >= 0 && protectedString[j] === "\\"; j--) {
|
|
50
|
+
backslashCount++
|
|
51
|
+
}
|
|
52
|
+
// Quote is escaped only if preceded by odd number of backslashes
|
|
53
|
+
const isEscaped = backslashCount % 2 === 1
|
|
54
|
+
|
|
55
|
+
if (!isEscaped) {
|
|
56
|
+
if (!inQuote) {
|
|
57
|
+
inQuote = true
|
|
58
|
+
quoteChar = char
|
|
59
|
+
} else if (char === quoteChar) {
|
|
60
|
+
inQuote = false
|
|
61
|
+
}
|
|
52
62
|
}
|
|
53
63
|
}
|
|
54
64
|
|
|
@@ -78,17 +88,17 @@ function splitByComma(string, regexPattern) {
|
|
|
78
88
|
}
|
|
79
89
|
}
|
|
80
90
|
|
|
81
|
-
// Process comma
|
|
91
|
+
// Process comma - use substring instead of char-by-char concat
|
|
82
92
|
if (char === "," && !inQuote && bracketDepth === 0 && dollarBraceDepth === 0) {
|
|
83
|
-
result.push(
|
|
84
|
-
|
|
85
|
-
} else {
|
|
86
|
-
current += char
|
|
93
|
+
result.push(protectedString.substring(segmentStart, i).trim())
|
|
94
|
+
segmentStart = i + 1
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
|
|
98
|
+
// Add final segment
|
|
99
|
+
const finalSegment = protectedString.substring(segmentStart).trim()
|
|
100
|
+
if (finalSegment || result.length > 0) {
|
|
101
|
+
result.push(finalSegment)
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
if (!regexPattern) {
|
|
@@ -125,5 +125,24 @@ test('splitByComma - should handle deeply nested variables with regex', () => {
|
|
|
125
125
|
assert.equal(result[3], '"three"')
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
+
test('splitByComma - should handle escaped backslash before closing quote', () => {
|
|
129
|
+
// 'text\\' = literal backslash at end of string, quote should close it
|
|
130
|
+
const result = splitByComma("before, 'text\\\\', after")
|
|
131
|
+
assert.equal(result, ["before", "'text\\\\'", "after"])
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('splitByComma - should handle multiple escaped backslashes before quote', () => {
|
|
135
|
+
// 'test\\\\' = two literal backslashes at end, quote should close it
|
|
136
|
+
const result = splitByComma("a, 'test\\\\\\\\', b")
|
|
137
|
+
assert.equal(result, ["a", "'test\\\\\\\\'", "b"])
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
test('splitByComma - should handle odd backslashes (escaped quote)', () => {
|
|
141
|
+
// 'test\\\' = one literal backslash + escaped quote, string not closed
|
|
142
|
+
// This should NOT split since the quote is escaped
|
|
143
|
+
const result = splitByComma("a, 'test\\\\\\'quoted', b")
|
|
144
|
+
assert.equal(result, ["a", "'test\\\\\\'quoted'", "b"])
|
|
145
|
+
})
|
|
146
|
+
|
|
128
147
|
// Run all tests
|
|
129
148
|
test.run()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* Splits string on single pipe (|) but preserves double pipes (||) */
|
|
2
|
+
|
|
3
|
+
const DOUBLE_PIPE_PLACEHOLDER = '\x00DOUBLE_PIPE\x00'
|
|
4
|
+
// Pre-compile regex for placeholder restoration (perf: avoid recompilation in map)
|
|
5
|
+
const DOUBLE_PIPE_REGEX = /\|\|/g
|
|
6
|
+
const PLACEHOLDER_RESTORE_REGEX = new RegExp(DOUBLE_PIPE_PLACEHOLDER, 'g')
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Splits a string on single pipe (|) characters while preserving double pipes (||).
|
|
10
|
+
* This is needed for filter parsing since || is a logical operator, not a filter delimiter.
|
|
11
|
+
* @param {string} str - String to split
|
|
12
|
+
* @returns {string[]} - Array of parts split on single |
|
|
13
|
+
*/
|
|
14
|
+
function splitOnPipe(str) {
|
|
15
|
+
if (!str || typeof str !== 'string') return [str]
|
|
16
|
+
|
|
17
|
+
// Replace || with placeholder, split on |, restore ||
|
|
18
|
+
DOUBLE_PIPE_REGEX.lastIndex = 0
|
|
19
|
+
const parts = str.replace(DOUBLE_PIPE_REGEX, DOUBLE_PIPE_PLACEHOLDER).split('|')
|
|
20
|
+
|
|
21
|
+
// Only restore placeholders if we actually had any
|
|
22
|
+
if (str.indexOf('||') === -1) return parts
|
|
23
|
+
|
|
24
|
+
return parts.map(s => {
|
|
25
|
+
PLACEHOLDER_RESTORE_REGEX.lastIndex = 0
|
|
26
|
+
return s.replace(PLACEHOLDER_RESTORE_REGEX, '||')
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { splitOnPipe }
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* Tests for splitOnPipe utility */
|
|
2
|
+
const { test } = require('uvu')
|
|
3
|
+
const assert = require('uvu/assert')
|
|
4
|
+
const { splitOnPipe } = require('./splitOnPipe')
|
|
5
|
+
|
|
6
|
+
test('splitOnPipe - single pipe', () => {
|
|
7
|
+
const result = splitOnPipe('a | b')
|
|
8
|
+
assert.equal(result, ['a ', ' b'])
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
test('splitOnPipe - multiple single pipes', () => {
|
|
12
|
+
const result = splitOnPipe('a | b | c')
|
|
13
|
+
assert.equal(result, ['a ', ' b ', ' c'])
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('splitOnPipe - preserves double pipe', () => {
|
|
17
|
+
const result = splitOnPipe('a || b')
|
|
18
|
+
assert.equal(result, ['a || b'])
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('splitOnPipe - eval with logical OR', () => {
|
|
22
|
+
const result = splitOnPipe('eval(true || undefined)')
|
|
23
|
+
assert.equal(result, ['eval(true || undefined)'])
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('splitOnPipe - mixed single and double pipes', () => {
|
|
27
|
+
const result = splitOnPipe('eval(a || b) | filter')
|
|
28
|
+
assert.equal(result, ['eval(a || b) ', ' filter'])
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('splitOnPipe - multiple double pipes', () => {
|
|
32
|
+
const result = splitOnPipe('a || b || c')
|
|
33
|
+
assert.equal(result, ['a || b || c'])
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('splitOnPipe - double pipe followed by single pipe', () => {
|
|
37
|
+
const result = splitOnPipe('a || b | c')
|
|
38
|
+
assert.equal(result, ['a || b ', ' c'])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('splitOnPipe - empty string', () => {
|
|
42
|
+
const result = splitOnPipe('')
|
|
43
|
+
assert.equal(result, [''])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('splitOnPipe - no pipes', () => {
|
|
47
|
+
const result = splitOnPipe('abc')
|
|
48
|
+
assert.equal(result, ['abc'])
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('splitOnPipe - null input', () => {
|
|
52
|
+
const result = splitOnPipe(null)
|
|
53
|
+
assert.equal(result, [null])
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('splitOnPipe - undefined input', () => {
|
|
57
|
+
const result = splitOnPipe(undefined)
|
|
58
|
+
assert.equal(result, [undefined])
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('splitOnPipe - bitwise OR should be preserved', () => {
|
|
62
|
+
// Note: bitwise | is a single pipe, so it WILL be split
|
|
63
|
+
// This is expected - bitwise OR in eval still won't work with filters
|
|
64
|
+
const result = splitOnPipe('eval(5 | 3)')
|
|
65
|
+
assert.equal(result, ['eval(5 ', ' 3)'])
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test.run()
|
|
@@ -24,7 +24,7 @@ test('isValidValue - should return true for non-empty array', () => {
|
|
|
24
24
|
assert.is(isValidValue([1, 2, 3]), true)
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
-
test
|
|
27
|
+
test('isValidValue - should return false for null', () => {
|
|
28
28
|
assert.is(isValidValue(null), false)
|
|
29
29
|
})
|
|
30
30
|
|
|
@@ -53,7 +53,9 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, variab
|
|
|
53
53
|
|
|
54
54
|
// Generate a unique placeholder
|
|
55
55
|
const placeholder = `__VAR_${iteration - 1}__`
|
|
56
|
-
|
|
56
|
+
// Pre-compile regex for this placeholder (perf: avoids recompilation in replaceAllPlaceholders)
|
|
57
|
+
const placeholderRegex = new RegExp(placeholder, 'g')
|
|
58
|
+
|
|
57
59
|
// Store match details
|
|
58
60
|
const matchInfo = {
|
|
59
61
|
variableType: undefined,
|
|
@@ -66,6 +68,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, variab
|
|
|
66
68
|
start: match.index,
|
|
67
69
|
end: match.index + match[0].length,
|
|
68
70
|
placeholder,
|
|
71
|
+
placeholderRegex,
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
if (debug) {
|
|
@@ -136,7 +139,9 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, variab
|
|
|
136
139
|
for (let i = 0; i < matchesArray.length; i++) {
|
|
137
140
|
const m = matchesArray[i]
|
|
138
141
|
if (result.includes(m.placeholder)) {
|
|
139
|
-
|
|
142
|
+
// Reset lastIndex before reusing global regex
|
|
143
|
+
m.placeholderRegex.lastIndex = 0
|
|
144
|
+
result = result.replace(m.placeholderRegex, m[key])
|
|
140
145
|
needsAnotherPass = true
|
|
141
146
|
}
|
|
142
147
|
}
|
|
@@ -219,6 +224,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, variab
|
|
|
219
224
|
|
|
220
225
|
const finalMatches = matches.map((m) => {
|
|
221
226
|
delete m.placeholder
|
|
227
|
+
delete m.placeholderRegex
|
|
222
228
|
if (typeof m.variableType === 'undefined') {
|
|
223
229
|
/*
|
|
224
230
|
{
|
package/types/src/main.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ declare class Configorama {
|
|
|
3
3
|
constructor(fileOrObject: any, opts: any);
|
|
4
4
|
settings: any;
|
|
5
5
|
filterCache: {};
|
|
6
|
+
_originalValueCache: Map<any, any>;
|
|
6
7
|
foundVariables: any[];
|
|
7
8
|
fileRefsFound: any[];
|
|
8
9
|
resolutionTracking: {};
|
|
@@ -12,6 +13,7 @@ declare class Configorama {
|
|
|
12
13
|
varPrefixPattern: RegExp;
|
|
13
14
|
varSuffixPattern: RegExp;
|
|
14
15
|
varSuffixWithSpacePattern: RegExp;
|
|
16
|
+
rawOriginalConfig: any;
|
|
15
17
|
config: any;
|
|
16
18
|
originalConfig: any;
|
|
17
19
|
configPath: any;
|
|
@@ -21,6 +23,7 @@ declare class Configorama {
|
|
|
21
23
|
tracker: PromiseTracker;
|
|
22
24
|
variableTypes: any;
|
|
23
25
|
variablesKnownTypes: RegExp;
|
|
26
|
+
_resolverByPrefix: Map<any, any>;
|
|
24
27
|
filters: any;
|
|
25
28
|
filterMatch: RegExp;
|
|
26
29
|
functions: any;
|
|
@@ -54,7 +57,6 @@ declare class Configorama {
|
|
|
54
57
|
init(cliOpts: any): Promise<any>;
|
|
55
58
|
options: any;
|
|
56
59
|
configFileContents: string;
|
|
57
|
-
rawOriginalConfig: any;
|
|
58
60
|
/**
|
|
59
61
|
* Collect metadata about all variables found in the configuration
|
|
60
62
|
* @returns {object} Metadata object containing variables, fileRefs, and summary
|
package/types/src/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/main.js"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/main.js"],"names":[],"mappings":";AA6GA;IACE,0CA4dC;IApdC,cAcW;IAuBX,gBAAqB;IAErB,mCAAoC;IAEpC,sBAAwB;IACxB,qBAAuB;IAGvB,uBAA4B;IAsB5B,uBAAoC;IAIpC,kBAAqC;IACrC,kBAAqC;IAErC,yBAA+F;IAC/F,yBAAuD;IACvD,kCAAyE;IAKvE,uBAAgD;IAKhD,YAAuB;IAEvB,oBAA0C;IAE1C,gBAAoD;IAOpD,uBAAkC;IAElC,uBAA8B;IAE9B,uBAAkC;IASpC,wBAAmC;IAGnC,mBAqHC;IAwED,4BAA8C;IAG9C,iCAAkC;IAalC,aA2EC;IAUD,oBAEC;IAGD,eAkDC;IAOD,YAAc;IACd,cAAgB;IAChB,kBAAkB;IAGpB;;;;OAIG;IACH,0BAHW,MAAM,GACJ,OAAO,CAQnB;IAED;;;;OAIG;IACH,6BAHW,MAAM,GACJ,MAAM,GAAC,IAAI,CAOvB;IAED;;;;OAIG;IACH,gCAHW,MAAM,GACJ,OAAO,CA2BnB;IAKD;;;;;OAKG;IACH,oBAFa,OAAO,CAAC,GAAG,CAAC,CAgvBxB;IA7uBC,aAA4B;IAc1B,2BAA4B;IAiuBhC;;;OAGG;IACH,2BAFa,MAAM,CA6alB;IAvBC;;;;;;;;;;;;;;;MAoBC;IAIH;;;;OAIG;IACH,uCAFa,OAAO,CAAC,GAAG,CAAC,CAIxB;IACD,+CAsBC;IAKD;;;;;;;;;;;;;;;;;;;OAmBG;IACH;;;;;;;;;;;OAWG;IACH,mFAHa;;;;cAZC,QAAQ;;;;eACR,IAAI,GAAC,MAAM,SAAO;OAWD,CA6E9B;IACD;;;OAGG;IACH;;;;;OAKG;IACH,oCAHa,OAAO,CAAC;;;;cAjGP,QAAQ;;;;eACR,IAAI,GAAC,MAAM,SAAO;OAgGgB,CAAC,EAAE,CA6BlD;IACD;;;;;OAKG;IACH,iDAFa,OAAO,CAAC,IAAI,CAAC,CAWzB;IAID;;;;;OAKG;IACH;;;;OAIG;IACH,2BAFa,eAAc;;;;;;;;;;OAAa,CAavC;IACD;;;;;OAKG;IACH,yBAHW;;;;;;;;;;OAAa,gCACX,cAAS,CAOrB;IACD;;;;;;OAMG;IACH,6DAFa,GAAC,CAiLb;IAKD;;;;;;;OAOG;IACH,yDAHa,OAAO,CAAC,GAAG,CAAC,CAiCxB;IACD;;;;OAIG;IAOH;;;;;;OAMG;IACH,wFA2BC;IACD;;;;;;;;;;;OAWG;IACH,8BARG;QAAyB,KAAK,EAAtB,GAAG;QACoB,IAAI,GAA3B,MAAM,EAAE;QACa,cAAc,GAAnC,MAAM;QACc,iBAAiB;KAC7C,6CAEU;QAAC,KAAK,EAAE,GAAG,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,QAAQ;QAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAC,CAma9J;IAID;;;;;;;;OAQG;IACH,qEAHa,OAAO,CAAC,GAAG,CAAC,CAoExB;IAKD;;;;;;;OAOG;IACH,0FAFa,OAAO,CAAC,GAAG,CAAC,CAiiBxB;IACD,+EA+BC;IACD,yDAiBC;IACD,oEA6BC;IAKD,8CAQC;IACD,kDAyBC;IACD;;;;;;;;;;;;;OAaG;IACH,wEAoDC;IAKD,4BAOC;IACD,sCAqEC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esm.d.ts","sourceRoot":"","sources":["../../../src/parsers/esm.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,yCAJW,MAAM,eAEJ,OAAO,CAAC,GAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"esm.d.ts","sourceRoot":"","sources":["../../../src/parsers/esm.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,yCAJW,MAAM,eAEJ,OAAO,CAAC,GAAC,CAAC,CAmBtB;AAED;;;;;GAKG;AACH,6CAJW,MAAM,eAEJ,GAAC,CAkBb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../../../src/parsers/typescript.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,gDAJW,MAAM,eAEJ,OAAO,CAAC,GAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../../../src/parsers/typescript.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,gDAJW,MAAM,eAEJ,OAAO,CAAC,GAAC,CAAC,CAmDtB;AAED;;;;;GAKG;AACH,oDAJW,MAAM,eAEJ,GAAC,CAmDb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valueFromEval.d.ts","sourceRoot":"","sources":["../../../src/resolvers/valueFromEval.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"valueFromEval.d.ts","sourceRoot":"","sources":["../../../src/resolvers/valueFromEval.js"],"names":[],"mappings":"AAQA,gDAIC;AAXD,oCAA+C;AAyC/C,qEA6CC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const ifRefSyntax: RegExp;
|
|
2
|
+
declare function getValueFromIf(variableString: any): Promise<any>;
|
|
3
|
+
export declare let type: string;
|
|
4
|
+
export declare let source: string;
|
|
5
|
+
export declare let description: string;
|
|
6
|
+
export { ifRefSyntax as match, getValueFromIf as resolver };
|
|
7
|
+
//# sourceMappingURL=valueFromIf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"valueFromIf.d.ts","sourceRoot":"","sources":["../../../src/resolvers/valueFromIf.js"],"names":[],"mappings":"AAOA,kCAAqD;AAErD,mEAyDC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valueFromNumber.d.ts","sourceRoot":"","sources":["../../../src/resolvers/valueFromNumber.js"],"names":[],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"valueFromNumber.d.ts","sourceRoot":"","sources":["../../../src/resolvers/valueFromNumber.js"],"names":[],"mappings":"AAEA,4DAMC;AAED,0EAEC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleSignalEvents.d.ts","sourceRoot":"","sources":["../../../src/utils/handleSignalEvents.js"],"names":[],"mappings":";AAEA,
|
|
1
|
+
{"version":3,"file":"handleSignalEvents.d.ts","sourceRoot":"","sources":["../../../src/utils/handleSignalEvents.js"],"names":[],"mappings":";AAEA,4CAqDC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lodash.d.ts","sourceRoot":"","sources":["../../../src/utils/lodash.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lodash.d.ts","sourceRoot":"","sources":["../../../src/utils/lodash.js"],"names":[],"mappings":"AAqDA,mDA4BC;AA/DD,6DA6BC"}
|
|
@@ -4,7 +4,11 @@ export = preProcess;
|
|
|
4
4
|
* @param {Object} configObject - The parsed configuration object
|
|
5
5
|
* @param {RegExp} variableSyntax - The variable syntax regex to use
|
|
6
6
|
* @param {Array} [variableTypes] - Array of variable type definitions with type/prefix fields
|
|
7
|
+
* @param {Object} [options] - Options for preprocessing
|
|
8
|
+
* @param {boolean} [options.skipFallbackFix] - Skip fixing malformed fallbacks (for object configs)
|
|
7
9
|
* @returns {Object} The preprocessed configuration object
|
|
8
10
|
*/
|
|
9
|
-
declare function preProcess(configObject: any, variableSyntax: RegExp, variableTypes?: any[]
|
|
11
|
+
declare function preProcess(configObject: any, variableSyntax: RegExp, variableTypes?: any[], options?: {
|
|
12
|
+
skipFallbackFix?: boolean;
|
|
13
|
+
}): any;
|
|
10
14
|
//# sourceMappingURL=preProcess.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preProcess.d.ts","sourceRoot":"","sources":["../../../../src/utils/parsing/preProcess.js"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"preProcess.d.ts","sourceRoot":"","sources":["../../../../src/utils/parsing/preProcess.js"],"names":[],"mappings":";AASA;;;;;;;;GAQG;AACH,+DANW,MAAM,mCAGd;IAA0B,eAAe,GAAjC,OAAO;CACf,OA0XF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFullFilePath.d.ts","sourceRoot":"","sources":["../../../../src/utils/paths/getFullFilePath.js"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"getFullFilePath.d.ts","sourceRoot":"","sources":["../../../../src/utils/paths/getFullFilePath.js"],"names":[],"mappings":";AAmCA,gEAIC;;;;AAED;;;;;;GAMG;AACH,6DALW,MAAM,UACN,MAAM,cACN,MAAM,GACJ;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAC,CAY9E;AAnDD;;;;;GAKG;AACH,gDAJW,MAAM,YACN,MAAM,GACJ,MAAM,CAsBlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/utils/regex/index.js"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/utils/regex/index.js"],"names":[],"mappings":";;IA+EQ,wCAAwC;;IAGpC,mCAAkC;;AAlF9C;;GAEG;AAGH,qCAA0D;AAC1D,0CAAgE;AAChE,sCAAiE;AA8EjE;;;;GAIG;AACH,wCAHW,MAAM,EAAE,GACN,MAAM,CAKlB;AApFD;;;;;;GAMG;AACH,uCAHW,MAAM,GACJ,GAAG,CAyDf"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find index of a character/pattern outside of quoted strings
|
|
3
|
+
* @param {string} str - String to search
|
|
4
|
+
* @param {string|function} matcher - Char to find, or function(str, idx) => matchLength|0
|
|
5
|
+
* @param {number} [startIdx=0] - Start index
|
|
6
|
+
* @returns {number} Index of match, or -1 if not found
|
|
7
|
+
*/
|
|
8
|
+
export function findOutsideQuotes(str: string, matcher: string | Function, startIdx?: number): number;
|
|
9
|
+
/**
|
|
10
|
+
* Replace a pattern only outside of quoted strings
|
|
11
|
+
* @param {string} str - String to process
|
|
12
|
+
* @param {string|RegExp} pattern - Pattern to match (if string, must be exact match)
|
|
13
|
+
* @param {string|function} replacement - Replacement string or function(match) => string
|
|
14
|
+
* @returns {string} Processed string
|
|
15
|
+
*/
|
|
16
|
+
export function replaceOutsideQuotes(str: string, pattern: string | RegExp, replacement: string | Function): string;
|
|
17
|
+
/**
|
|
18
|
+
* Check if an index is inside a quoted string
|
|
19
|
+
* @param {string} str - String to check
|
|
20
|
+
* @param {number} idx - Index to check
|
|
21
|
+
* @returns {boolean} True if index is inside quotes
|
|
22
|
+
*/
|
|
23
|
+
export function isInsideQuotes(str: string, idx: number): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Get ranges of quoted strings in a string
|
|
26
|
+
* @param {string} str - String to analyze
|
|
27
|
+
* @returns {Array<[number, number]>} Array of [start, end] ranges
|
|
28
|
+
*/
|
|
29
|
+
export function getQuoteRanges(str: string): Array<[number, number]>;
|
|
30
|
+
//# sourceMappingURL=quoteAware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quoteAware.d.ts","sourceRoot":"","sources":["../../../../src/utils/strings/quoteAware.js"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,uCALW,MAAM,WACN,MAAM,WAAS,aACf,MAAM,GACJ,MAAM,CAyBlB;AAED;;;;;;GAMG;AACH,0CALW,MAAM,WACN,MAAM,GAAC,MAAM,eACb,MAAM,WAAS,GACb,MAAM,CA4ClB;AAED;;;;;GAKG;AACH,oCAJW,MAAM,OACN,MAAM,GACJ,OAAO,CAiBnB;AAED;;;;GAIG;AACH,oCAHW,MAAM,GACJ,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAsBnC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replaceAll.d.ts","sourceRoot":"","sources":["../../../../src/utils/strings/replaceAll.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"replaceAll.d.ts","sourceRoot":"","sources":["../../../../src/utils/strings/replaceAll.js"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,wCALW,MAAM,YACN,MAAM,UACN,MAAM,GACJ,MAAM,CAelB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export function splitByComma(string: any, regexPattern: any):
|
|
1
|
+
export function splitByComma(string: any, regexPattern: any): any[];
|
|
2
2
|
//# sourceMappingURL=splitByComma.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"splitByComma.d.ts","sourceRoot":"","sources":["../../../../src/utils/strings/splitByComma.js"],"names":[],"mappings":"AAiBA,
|
|
1
|
+
{"version":3,"file":"splitByComma.d.ts","sourceRoot":"","sources":["../../../../src/utils/strings/splitByComma.js"],"names":[],"mappings":"AAiBA,oEAgGC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splits a string on single pipe (|) characters while preserving double pipes (||).
|
|
3
|
+
* This is needed for filter parsing since || is a logical operator, not a filter delimiter.
|
|
4
|
+
* @param {string} str - String to split
|
|
5
|
+
* @returns {string[]} - Array of parts split on single |
|
|
6
|
+
*/
|
|
7
|
+
export function splitOnPipe(str: string): string[];
|
|
8
|
+
//# sourceMappingURL=splitOnPipe.d.ts.map
|