configorama 0.11.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +429 -123
- package/cli.js +282 -49
- package/index.d.ts +43 -1
- package/package.json +5 -1
- package/src/capabilities.js +59 -0
- package/src/capabilities.test.js +44 -0
- package/src/display.js +70 -7
- package/src/display.test.js +82 -0
- package/src/errors.js +73 -0
- package/src/index.js +91 -1
- package/src/main.js +117 -15
- package/src/resolvers/valueFromEval.js +11 -1
- package/src/resolvers/valueFromFile.js +5 -0
- package/src/resolvers/valueFromGit.js +43 -17
- package/src/resolvers/valueFromOptions.js +5 -4
- package/src/utils/filters/filterArgs.js +57 -0
- package/src/utils/filters/oneOf.js +77 -0
- package/src/utils/introspection/audit.js +78 -0
- package/src/utils/introspection/graph.js +43 -0
- package/src/utils/introspection/model.js +150 -0
- package/src/utils/introspection/model.test.js +93 -0
- package/src/utils/parsing/commentAnnotations.js +107 -0
- package/src/utils/parsing/commentAnnotations.test.js +123 -0
- package/src/utils/parsing/enrichMetadata.js +64 -1
- package/src/utils/parsing/enrichMetadata.test.js +84 -0
- package/src/utils/parsing/extractComment.js +145 -0
- package/src/utils/parsing/extractComment.test.js +182 -0
- package/src/utils/parsing/preProcess.js +2 -1
- package/src/utils/paths/findLineForKey.js +2 -2
- package/src/utils/paths/ignorePaths.js +21 -9
- package/src/utils/redaction/redact.js +78 -0
- package/src/utils/redaction/redact.test.js +38 -0
- package/src/utils/redaction/setupRedaction.js +47 -0
- package/src/utils/redaction/setupRedaction.test.js +68 -0
- package/src/utils/requirements/configRequirements.js +351 -0
- package/src/utils/requirements/configRequirements.test.js +380 -0
- package/src/utils/requirements/serializeRequirements.js +120 -0
- package/src/utils/requirements/serializeRequirements.test.js +211 -0
- package/src/utils/security/evalSafety.js +86 -0
- package/src/utils/security/evalSafety.test.js +61 -0
- package/src/utils/security/safetyPolicy.js +110 -0
- package/src/utils/security/safetyPolicy.test.js +29 -0
- package/src/utils/strings/didYouMean.js +70 -0
- package/src/utils/strings/didYouMean.test.js +52 -0
- package/src/utils/strings/formatFunctionArgs.js +6 -1
- package/src/utils/strings/splitByComma.js +5 -0
- package/src/utils/ui/configWizard.js +208 -34
- package/src/utils/ui/createEditorLink.js +17 -1
- package/src/utils/ui/promptDescriptors.js +196 -0
- package/src/utils/ui/promptDescriptors.test.js +162 -0
- package/src/utils/variables/cleanVariable.js +22 -0
- package/src/utils/variables/getVariableType.js +1 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const { test } = require('uvu')
|
|
2
|
+
const assert = require('uvu/assert')
|
|
3
|
+
const {
|
|
4
|
+
isSensitiveVariable,
|
|
5
|
+
redactObjectByPaths,
|
|
6
|
+
REDACTED_VALUE,
|
|
7
|
+
} = require('./redact')
|
|
8
|
+
|
|
9
|
+
test('isSensitiveVariable detects common secret names', () => {
|
|
10
|
+
assert.is(isSensitiveVariable('API_KEY'), true)
|
|
11
|
+
assert.is(isSensitiveVariable('clientSecret'), true)
|
|
12
|
+
assert.is(isSensitiveVariable('private_key'), true)
|
|
13
|
+
assert.is(isSensitiveVariable('region'), false)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('isSensitiveVariable honors annotation overrides', () => {
|
|
17
|
+
assert.is(isSensitiveVariable('API_KEY', {
|
|
18
|
+
sensitiveEntries: [{ value: false, path: 'apiKey' }]
|
|
19
|
+
}), false)
|
|
20
|
+
|
|
21
|
+
assert.is(isSensitiveVariable('region', {
|
|
22
|
+
sensitiveEntries: [{ value: true, path: 'region' }]
|
|
23
|
+
}), true)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('redactObjectByPaths redacts nested config paths', () => {
|
|
27
|
+
const redacted = redactObjectByPaths({
|
|
28
|
+
service: 'demo',
|
|
29
|
+
secrets: {
|
|
30
|
+
apiKey: 'secret-value'
|
|
31
|
+
}
|
|
32
|
+
}, ['secrets.apiKey'])
|
|
33
|
+
|
|
34
|
+
assert.is(redacted.service, 'demo')
|
|
35
|
+
assert.is(redacted.secrets.apiKey, REDACTED_VALUE)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test.run()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const dotProp = require('dot-prop')
|
|
2
|
+
const { REDACTED_VALUE, cloneJson } = require('./redact')
|
|
3
|
+
|
|
4
|
+
function inputSectionForRequirement(requirement) {
|
|
5
|
+
if (!requirement) return null
|
|
6
|
+
if (requirement.variableType === 'option') return 'options'
|
|
7
|
+
if (requirement.variableType === 'env') return 'env'
|
|
8
|
+
if (requirement.variableType === 'self') return 'self'
|
|
9
|
+
if (requirement.variableType === 'dotProp') return 'dotProp'
|
|
10
|
+
return null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function redactUserInputsByRequirements(userInputs, requirements) {
|
|
14
|
+
const redacted = cloneJson(userInputs || {})
|
|
15
|
+
|
|
16
|
+
for (const requirement of requirements || []) {
|
|
17
|
+
if (!requirement || requirement.sensitive !== true) continue
|
|
18
|
+
const section = inputSectionForRequirement(requirement)
|
|
19
|
+
if (!section || !redacted[section]) continue
|
|
20
|
+
if (Object.prototype.hasOwnProperty.call(redacted[section], requirement.name)) {
|
|
21
|
+
redacted[section][requirement.name] = REDACTED_VALUE
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return redacted
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function redactConfigByRequirements(config, requirements) {
|
|
29
|
+
const redacted = cloneJson(config)
|
|
30
|
+
|
|
31
|
+
for (const requirement of requirements || []) {
|
|
32
|
+
if (!requirement || requirement.sensitive !== true) continue
|
|
33
|
+
for (const configPath of requirement.paths || []) {
|
|
34
|
+
if (configPath && dotProp.has(redacted, configPath)) {
|
|
35
|
+
dotProp.set(redacted, configPath, REDACTED_VALUE)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return redacted
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = {
|
|
44
|
+
REDACTED_VALUE,
|
|
45
|
+
redactConfigByRequirements,
|
|
46
|
+
redactUserInputsByRequirements,
|
|
47
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const { test } = require('uvu')
|
|
2
|
+
const assert = require('uvu/assert')
|
|
3
|
+
const {
|
|
4
|
+
REDACTED_VALUE,
|
|
5
|
+
redactConfigByRequirements,
|
|
6
|
+
redactUserInputsByRequirements,
|
|
7
|
+
} = require('./setupRedaction')
|
|
8
|
+
|
|
9
|
+
const requirements = [
|
|
10
|
+
{
|
|
11
|
+
name: 'API_KEY',
|
|
12
|
+
variableType: 'env',
|
|
13
|
+
sensitive: true,
|
|
14
|
+
paths: ['secrets.apiKey'],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'PUBLIC_KEY',
|
|
18
|
+
variableType: 'env',
|
|
19
|
+
sensitive: false,
|
|
20
|
+
paths: ['secrets.publicKey'],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'stage',
|
|
24
|
+
variableType: 'option',
|
|
25
|
+
sensitive: false,
|
|
26
|
+
paths: ['stage'],
|
|
27
|
+
},
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
test('redactUserInputsByRequirements redacts only sensitive setup values', () => {
|
|
31
|
+
const inputs = {
|
|
32
|
+
options: { stage: 'prod' },
|
|
33
|
+
env: {
|
|
34
|
+
API_KEY: 'secret-value',
|
|
35
|
+
PUBLIC_KEY: 'public-value',
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
assert.equal(redactUserInputsByRequirements(inputs, requirements), {
|
|
40
|
+
options: { stage: 'prod' },
|
|
41
|
+
env: {
|
|
42
|
+
API_KEY: REDACTED_VALUE,
|
|
43
|
+
PUBLIC_KEY: 'public-value',
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
assert.is(inputs.env.API_KEY, 'secret-value')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('redactConfigByRequirements redacts only sensitive resolved config paths', () => {
|
|
50
|
+
const config = {
|
|
51
|
+
stage: 'prod',
|
|
52
|
+
secrets: {
|
|
53
|
+
apiKey: 'secret-value',
|
|
54
|
+
publicKey: 'public-value',
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
assert.equal(redactConfigByRequirements(config, requirements), {
|
|
59
|
+
stage: 'prod',
|
|
60
|
+
secrets: {
|
|
61
|
+
apiKey: REDACTED_VALUE,
|
|
62
|
+
publicKey: 'public-value',
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
assert.is(config.secrets.apiKey, 'secret-value')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test.run()
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
const { isSensitiveVariable } = require('../redaction/redact')
|
|
2
|
+
|
|
3
|
+
const TYPE_MAP = {
|
|
4
|
+
Boolean: 'boolean',
|
|
5
|
+
String: 'string',
|
|
6
|
+
Number: 'number',
|
|
7
|
+
Array: 'array',
|
|
8
|
+
Object: 'object',
|
|
9
|
+
Json: 'json',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const VARIABLE_TYPE_MAP = {
|
|
13
|
+
options: 'option',
|
|
14
|
+
opt: 'option',
|
|
15
|
+
option: 'option',
|
|
16
|
+
'dot.prop': 'dotProp',
|
|
17
|
+
dotProp: 'dotProp',
|
|
18
|
+
if: 'eval',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeVariableType(variableType) {
|
|
22
|
+
return VARIABLE_TYPE_MAP[variableType] || variableType || 'unknown'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizeType(type) {
|
|
26
|
+
if (!type) return 'string'
|
|
27
|
+
return TYPE_MAP[type] || String(type).toLowerCase()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function cleanDefaultValue(value) {
|
|
31
|
+
if (value === undefined || value === null) return null
|
|
32
|
+
if (typeof value !== 'string') return value
|
|
33
|
+
const match = value.match(/^(['"])([\s\S]*)\1$/)
|
|
34
|
+
return match ? match[2] : value
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function unique(values) {
|
|
38
|
+
return [...new Set(values.filter(value => value !== undefined && value !== null))]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function uniqueBy(values, getKey) {
|
|
42
|
+
const seen = new Set()
|
|
43
|
+
const result = []
|
|
44
|
+
for (const value of values) {
|
|
45
|
+
const key = getKey(value)
|
|
46
|
+
if (seen.has(key)) continue
|
|
47
|
+
seen.add(key)
|
|
48
|
+
result.push(value)
|
|
49
|
+
}
|
|
50
|
+
return result
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function firstValue(values) {
|
|
54
|
+
return values.find(value => value !== undefined && value !== null)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getRequirementName(variable, variableType) {
|
|
58
|
+
const normalizedType = normalizeVariableType(variableType)
|
|
59
|
+
if (!variable) return ''
|
|
60
|
+
|
|
61
|
+
if (normalizedType === 'option') return variable.replace(/^(opt|option|options):/, '')
|
|
62
|
+
if (normalizedType === 'env') return variable.replace(/^env:/, '')
|
|
63
|
+
if (normalizedType === 'param') return variable.replace(/^param:/, '')
|
|
64
|
+
if (normalizedType === 'self') return variable.replace(/^self:/, '')
|
|
65
|
+
if (normalizedType === 'file' || normalizedType === 'text') {
|
|
66
|
+
const match = variable.match(/^(?:file|text)\((.+?)\)/)
|
|
67
|
+
return match ? match[1] : variable
|
|
68
|
+
}
|
|
69
|
+
return variable
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getOccurrenceTypes(occurrences) {
|
|
73
|
+
return unique((occurrences || []).map(occ => occ.type))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getOccurrenceTypeEntries(occurrences) {
|
|
77
|
+
return (occurrences || [])
|
|
78
|
+
.filter(occ => occ.type)
|
|
79
|
+
.map(occ => ({
|
|
80
|
+
value: normalizeType(occ.type),
|
|
81
|
+
path: occ.path,
|
|
82
|
+
}))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getDescriptionCandidates(entry, occurrences) {
|
|
86
|
+
const candidates = []
|
|
87
|
+
if (entry.description) {
|
|
88
|
+
candidates.push({
|
|
89
|
+
value: entry.description,
|
|
90
|
+
source: entry.descriptionSource || 'help',
|
|
91
|
+
path: null,
|
|
92
|
+
index: candidates.length,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
for (const occ of occurrences || []) {
|
|
96
|
+
if (!occ.description) continue
|
|
97
|
+
candidates.push({
|
|
98
|
+
value: occ.description,
|
|
99
|
+
source: occ.descriptionSource || 'help',
|
|
100
|
+
path: occ.path,
|
|
101
|
+
index: candidates.length,
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
if (entry.descriptions && entry.descriptions.length) {
|
|
105
|
+
const occurrenceDescriptions = new Set(candidates.map(candidate => candidate.value))
|
|
106
|
+
for (const description of entry.descriptions) {
|
|
107
|
+
if (occurrenceDescriptions.has(description)) continue
|
|
108
|
+
candidates.push({
|
|
109
|
+
value: description,
|
|
110
|
+
source: 'help',
|
|
111
|
+
path: null,
|
|
112
|
+
index: candidates.length,
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return uniqueBy(candidates, candidate => `${candidate.source}:${candidate.value}:${candidate.path || ''}`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getDescriptionPriority(source) {
|
|
120
|
+
if (source === 'commentTag') return -1
|
|
121
|
+
if (source === 'help') return 0
|
|
122
|
+
if (source === 'inlineComment' || source === 'comment') return 1
|
|
123
|
+
if (source === 'leadingComment') return 2
|
|
124
|
+
return 3
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function selectDescription(candidates) {
|
|
128
|
+
const sorted = [...candidates].sort((a, b) => {
|
|
129
|
+
const priority = getDescriptionPriority(a.source) - getDescriptionPriority(b.source)
|
|
130
|
+
if (priority !== 0) return priority
|
|
131
|
+
return a.index - b.index
|
|
132
|
+
})
|
|
133
|
+
return sorted[0] || null
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getOccurrenceDefaults(entry, occurrences) {
|
|
137
|
+
const defaults = (occurrences || []).map(occ => occ.defaultValue)
|
|
138
|
+
if (entry.resolvedValue !== undefined) defaults.push(entry.resolvedValue)
|
|
139
|
+
return defaults
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function getOccurrenceDefaultEntries(entry, occurrences) {
|
|
143
|
+
const entries = (occurrences || [])
|
|
144
|
+
.filter(occ => occ.defaultValue !== undefined && occ.defaultValue !== null)
|
|
145
|
+
.map(occ => ({
|
|
146
|
+
value: cleanDefaultValue(occ.defaultValue),
|
|
147
|
+
path: occ.path,
|
|
148
|
+
}))
|
|
149
|
+
if (entry.resolvedValue !== undefined && entry.resolvedValue !== null) {
|
|
150
|
+
entries.push({
|
|
151
|
+
value: cleanDefaultValue(entry.resolvedValue),
|
|
152
|
+
path: null,
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
return entries
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function getAllowedValues(entry, occurrences) {
|
|
159
|
+
if (entry.allowedValues) return entry.allowedValues
|
|
160
|
+
const values = (occurrences || []).flatMap(occ => occ.allowedValues || [])
|
|
161
|
+
return values.length ? unique(values).map(String) : null
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function collectFieldEntries(entry, occurrences, field) {
|
|
165
|
+
const entries = []
|
|
166
|
+
if (entry[field] !== undefined && entry[field] !== null) {
|
|
167
|
+
entries.push({
|
|
168
|
+
value: entry[field],
|
|
169
|
+
path: null,
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
for (const occ of occurrences || []) {
|
|
173
|
+
if (occ[field] === undefined || occ[field] === null) continue
|
|
174
|
+
entries.push({
|
|
175
|
+
value: occ[field],
|
|
176
|
+
path: occ.path,
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
return entries
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function selectFieldValue(entries) {
|
|
183
|
+
const entry = (entries || []).find(item => item.value !== undefined && item.value !== null)
|
|
184
|
+
return entry ? entry.value : null
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function mergeExamples(entry, occurrences) {
|
|
188
|
+
const values = []
|
|
189
|
+
if (entry.examples) values.push(...entry.examples)
|
|
190
|
+
for (const occ of occurrences || []) {
|
|
191
|
+
if (occ.examples) values.push(...occ.examples)
|
|
192
|
+
}
|
|
193
|
+
const merged = unique(values.map(String))
|
|
194
|
+
return merged.length ? merged : null
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function collectScalarAnnotationConflicts(fieldEntriesByField) {
|
|
198
|
+
const conflicts = []
|
|
199
|
+
for (const [field, entries] of Object.entries(fieldEntriesByField)) {
|
|
200
|
+
const uniqueEntries = uniqueBy(entries, entry => String(entry.value))
|
|
201
|
+
if (uniqueEntries.length > 1) {
|
|
202
|
+
conflicts.push(makeConflict(field, uniqueEntries))
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return conflicts
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function normalizeAllowedSet(values) {
|
|
209
|
+
return (values || []).map(String).sort()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function allowedSetKey(values) {
|
|
213
|
+
return JSON.stringify(normalizeAllowedSet(values))
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function collectAllowedSets(entry, occurrences) {
|
|
217
|
+
const sets = []
|
|
218
|
+
if (entry.allowedValues) {
|
|
219
|
+
sets.push({ values: entry.allowedValues, path: null })
|
|
220
|
+
}
|
|
221
|
+
for (const occ of occurrences || []) {
|
|
222
|
+
if (occ.allowedValues) {
|
|
223
|
+
sets.push({ values: occ.allowedValues, path: occ.path })
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return sets
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function getSourceClass(entry) {
|
|
230
|
+
return entry.variableSourceType || entry.sourceClass || null
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function makeConflict(field, values) {
|
|
234
|
+
return {
|
|
235
|
+
field,
|
|
236
|
+
values,
|
|
237
|
+
paths: unique(values.flatMap(value => value.paths || (value.path ? [value.path] : []))),
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function collectConflicts({ typeEntries, defaultEntries, allowedSets, scalarAnnotationEntries = {} }) {
|
|
242
|
+
const conflicts = []
|
|
243
|
+
|
|
244
|
+
const typedValues = uniqueBy(typeEntries, entry => entry.value)
|
|
245
|
+
if (typedValues.length > 1) {
|
|
246
|
+
conflicts.push(makeConflict('type', typedValues))
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const defaultValues = uniqueBy(defaultEntries.filter(entry => entry.value !== null), entry => String(entry.value))
|
|
250
|
+
if (defaultValues.length > 1) {
|
|
251
|
+
conflicts.push(makeConflict('default', defaultValues))
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const uniqueAllowedSets = uniqueBy(allowedSets, set => allowedSetKey(set.values))
|
|
255
|
+
if (uniqueAllowedSets.length > 1) {
|
|
256
|
+
conflicts.push(makeConflict('allowedValues', uniqueAllowedSets.map(set => ({
|
|
257
|
+
value: normalizeAllowedSet(set.values),
|
|
258
|
+
path: set.path,
|
|
259
|
+
}))))
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
conflicts.push(...collectScalarAnnotationConflicts(scalarAnnotationEntries))
|
|
263
|
+
|
|
264
|
+
return conflicts
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function getSensitiveValue(name, sensitiveEntries) {
|
|
268
|
+
return isSensitiveVariable(name, { sensitiveEntries })
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function buildRequirement(varKey, entry) {
|
|
272
|
+
const occurrences = entry.occurrences || []
|
|
273
|
+
const variableType = normalizeVariableType(entry.variableType)
|
|
274
|
+
const name = getRequirementName(entry.variable || varKey, entry.variableType)
|
|
275
|
+
const types = getOccurrenceTypes(occurrences)
|
|
276
|
+
const typeEntries = getOccurrenceTypeEntries(occurrences)
|
|
277
|
+
const descriptionCandidates = getDescriptionCandidates(entry, occurrences)
|
|
278
|
+
const selectedDescription = selectDescription(descriptionCandidates)
|
|
279
|
+
const defaults = getOccurrenceDefaults(entry, occurrences)
|
|
280
|
+
const defaultEntries = getOccurrenceDefaultEntries(entry, occurrences)
|
|
281
|
+
const defaultValue = cleanDefaultValue(firstValue(defaults))
|
|
282
|
+
const allowedSets = collectAllowedSets(entry, occurrences)
|
|
283
|
+
const obtainHintEntries = collectFieldEntries(entry, occurrences, 'obtainHint')
|
|
284
|
+
const defaultHintEntries = collectFieldEntries(entry, occurrences, 'defaultHint')
|
|
285
|
+
const groupEntries = collectFieldEntries(entry, occurrences, 'group')
|
|
286
|
+
const deprecationEntries = collectFieldEntries(entry, occurrences, 'deprecationMessage')
|
|
287
|
+
const sensitiveEntries = collectFieldEntries(entry, occurrences, 'sensitive')
|
|
288
|
+
const conflicts = collectConflicts({
|
|
289
|
+
typeEntries,
|
|
290
|
+
defaultEntries,
|
|
291
|
+
allowedSets,
|
|
292
|
+
scalarAnnotationEntries: {
|
|
293
|
+
obtainHint: obtainHintEntries,
|
|
294
|
+
defaultHint: defaultHintEntries,
|
|
295
|
+
group: groupEntries,
|
|
296
|
+
deprecationMessage: deprecationEntries,
|
|
297
|
+
sensitive: sensitiveEntries,
|
|
298
|
+
},
|
|
299
|
+
})
|
|
300
|
+
const obtainHint = selectFieldValue(obtainHintEntries)
|
|
301
|
+
const defaultHint = selectFieldValue(defaultHintEntries)
|
|
302
|
+
const group = selectFieldValue(groupEntries)
|
|
303
|
+
const deprecationMessage = selectFieldValue(deprecationEntries)
|
|
304
|
+
const sensitive = getSensitiveValue(name, sensitiveEntries)
|
|
305
|
+
const sensitiveSource = sensitiveEntries.length ? 'commentTag' : null
|
|
306
|
+
|
|
307
|
+
const paths = unique(occurrences.map(occ => occ.path))
|
|
308
|
+
const required = occurrences.some(occ => occ.isRequired === true) && defaultValue === null
|
|
309
|
+
const description = selectedDescription ? selectedDescription.value : null
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
name,
|
|
313
|
+
variable: entry.variable || varKey,
|
|
314
|
+
variableType,
|
|
315
|
+
sourceClass: getSourceClass(entry),
|
|
316
|
+
type: normalizeType(firstValue(types)),
|
|
317
|
+
description,
|
|
318
|
+
descriptionSource: selectedDescription ? selectedDescription.source : null,
|
|
319
|
+
allowedValues: getAllowedValues(entry, occurrences),
|
|
320
|
+
sensitive,
|
|
321
|
+
sensitiveSource,
|
|
322
|
+
required,
|
|
323
|
+
default: defaultValue,
|
|
324
|
+
defaultHint,
|
|
325
|
+
obtainHint,
|
|
326
|
+
examples: mergeExamples(entry, occurrences),
|
|
327
|
+
group,
|
|
328
|
+
deprecationMessage,
|
|
329
|
+
fileExists: entry.fileExists,
|
|
330
|
+
innerVariables: entry.innerVariables || [],
|
|
331
|
+
paths,
|
|
332
|
+
conflicts,
|
|
333
|
+
occurrences,
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function buildConfigRequirements(analysis) {
|
|
338
|
+
const uniqueVariables = analysis && analysis.uniqueVariables ? analysis.uniqueVariables : {}
|
|
339
|
+
return Object.entries(uniqueVariables).map(([varKey, entry]) => {
|
|
340
|
+
return buildRequirement(varKey, entry)
|
|
341
|
+
})
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
module.exports = {
|
|
345
|
+
buildConfigRequirements,
|
|
346
|
+
buildRequirement,
|
|
347
|
+
cleanDefaultValue,
|
|
348
|
+
collectConflicts,
|
|
349
|
+
normalizeType,
|
|
350
|
+
normalizeVariableType,
|
|
351
|
+
}
|