configorama 1.1.0 → 1.2.2
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 +10 -1
- package/cli.js +14 -1
- package/package.json +9 -4
- package/plugins/onepassword/index.js +15 -4
- package/plugins/onepassword/normalize.js +13 -1
- package/src/errors.js +3 -0
- package/src/index.js +19 -0
- package/src/main.js +18 -29
- package/src/resolvers/valueFromCron.js +19 -37
- package/src/utils/parsing/detectFormat.js +70 -0
- package/src/utils/parsing/detectFormat.test.js +68 -0
- package/src/utils/parsing/parse.js +1 -35
- package/src/utils/setup/applyAnswers.js +41 -0
- package/src/utils/setup/setupEngine.js +85 -0
- package/src/utils/setup/stdoutRedirect.js +48 -0
- package/src/utils/setup/writeAnswers.js +50 -0
- package/src/utils/setup/writeDotenv.js +142 -0
- package/src/utils/ui/configWizard.js +20 -1
- package/types/src/capabilities.d.ts +8 -0
- package/types/src/capabilities.d.ts.map +1 -0
- package/types/src/display.d.ts +62 -0
- package/types/src/display.d.ts.map +1 -0
- package/types/src/errors.d.ts +20 -0
- package/types/src/errors.d.ts.map +1 -0
- package/types/src/index.d.ts +119 -0
- package/types/src/index.d.ts.map +1 -1
- package/types/src/main.d.ts +23 -16
- package/types/src/main.d.ts.map +1 -1
- package/types/src/metadata.d.ts +28 -0
- package/types/src/metadata.d.ts.map +1 -0
- package/types/src/parsers/dotenv.d.ts +6 -0
- package/types/src/parsers/dotenv.d.ts.map +1 -0
- package/types/src/parsers/esm.d.ts.map +1 -1
- package/types/src/parsers/typescript.d.ts.map +1 -1
- package/types/src/resolvers/valueFromCron.d.ts.map +1 -1
- package/types/src/resolvers/valueFromEval.d.ts.map +1 -1
- package/types/src/resolvers/valueFromFile.d.ts +4 -0
- package/types/src/resolvers/valueFromFile.d.ts.map +1 -1
- package/types/src/resolvers/valueFromGit.d.ts.map +1 -1
- package/types/src/resolvers/valueFromOptions.d.ts +1 -0
- package/types/src/resolvers/valueFromOptions.d.ts.map +1 -1
- package/types/src/sync.d.ts.map +1 -1
- package/types/src/utils/BoundedMap.d.ts +10 -0
- package/types/src/utils/BoundedMap.d.ts.map +1 -0
- package/types/src/utils/PromiseTracker.d.ts +1 -1
- package/types/src/utils/PromiseTracker.d.ts.map +1 -1
- package/types/src/utils/filters/filterArgs.d.ts +13 -0
- package/types/src/utils/filters/filterArgs.d.ts.map +1 -0
- package/types/src/utils/filters/oneOf.d.ts +7 -0
- package/types/src/utils/filters/oneOf.d.ts.map +1 -0
- package/types/src/utils/introspection/audit.d.ts +14 -0
- package/types/src/utils/introspection/audit.d.ts.map +1 -0
- package/types/src/utils/introspection/graph.d.ts +4 -0
- package/types/src/utils/introspection/graph.d.ts.map +1 -0
- package/types/src/utils/introspection/model.d.ts +43 -0
- package/types/src/utils/introspection/model.d.ts.map +1 -0
- package/types/src/utils/parsing/commentAnnotations.d.ts +18 -0
- package/types/src/utils/parsing/commentAnnotations.d.ts.map +1 -0
- package/types/src/utils/parsing/detectFormat.d.ts +17 -0
- package/types/src/utils/parsing/detectFormat.d.ts.map +1 -0
- package/types/src/utils/parsing/enrichMetadata.d.ts.map +1 -1
- package/types/src/utils/parsing/extractComment.d.ts +20 -0
- package/types/src/utils/parsing/extractComment.d.ts.map +1 -0
- package/types/src/utils/parsing/parse.d.ts.map +1 -1
- package/types/src/utils/parsing/preProcess.d.ts.map +1 -1
- package/types/src/utils/paths/fileType.d.ts +15 -0
- package/types/src/utils/paths/fileType.d.ts.map +1 -0
- package/types/src/utils/paths/findLineForKey.d.ts +9 -0
- package/types/src/utils/paths/findLineForKey.d.ts.map +1 -1
- package/types/src/utils/paths/ignorePaths.d.ts +5 -0
- package/types/src/utils/paths/ignorePaths.d.ts.map +1 -0
- package/types/src/utils/redaction/redact.d.ts +10 -0
- package/types/src/utils/redaction/redact.d.ts.map +1 -0
- package/types/src/utils/redaction/setupRedaction.d.ts +5 -0
- package/types/src/utils/redaction/setupRedaction.d.ts.map +1 -0
- package/types/src/utils/requirements/configRequirements.d.ts +70 -0
- package/types/src/utils/requirements/configRequirements.d.ts.map +1 -0
- package/types/src/utils/requirements/serializeRequirements.d.ts +63 -0
- package/types/src/utils/requirements/serializeRequirements.d.ts.map +1 -0
- package/types/src/utils/security/dotenvFileRefs.d.ts +13 -0
- package/types/src/utils/security/dotenvFileRefs.d.ts.map +1 -0
- package/types/src/utils/security/evalSafety.d.ts +17 -0
- package/types/src/utils/security/evalSafety.d.ts.map +1 -0
- package/types/src/utils/security/safetyPolicy.d.ts +15 -0
- package/types/src/utils/security/safetyPolicy.d.ts.map +1 -0
- package/types/src/utils/setup/applyAnswers.d.ts +34 -0
- package/types/src/utils/setup/applyAnswers.d.ts.map +1 -0
- package/types/src/utils/setup/setupEngine.d.ts +89 -0
- package/types/src/utils/setup/setupEngine.d.ts.map +1 -0
- package/types/src/utils/setup/stdoutRedirect.d.ts +10 -0
- package/types/src/utils/setup/stdoutRedirect.d.ts.map +1 -0
- package/types/src/utils/setup/writeAnswers.d.ts +17 -0
- package/types/src/utils/setup/writeAnswers.d.ts.map +1 -0
- package/types/src/utils/setup/writeDotenv.d.ts +27 -0
- package/types/src/utils/setup/writeDotenv.d.ts.map +1 -0
- package/types/src/utils/strings/didYouMean.d.ts +23 -0
- package/types/src/utils/strings/didYouMean.d.ts.map +1 -0
- package/types/src/utils/strings/formatFunctionArgs.d.ts.map +1 -1
- package/types/src/utils/strings/replaceAll.d.ts.map +1 -1
- package/types/src/utils/strings/splitByComma.d.ts.map +1 -1
- package/types/src/utils/ui/configWizard.d.ts +51 -7
- package/types/src/utils/ui/configWizard.d.ts.map +1 -1
- package/types/src/utils/ui/createEditorLink.d.ts +7 -0
- package/types/src/utils/ui/createEditorLink.d.ts.map +1 -1
- package/types/src/utils/ui/promptDescriptors.d.ts +39 -0
- package/types/src/utils/ui/promptDescriptors.d.ts.map +1 -0
- package/types/src/utils/variables/cleanVariable.d.ts.map +1 -1
- package/types/src/utils/variables/getVariableType.d.ts.map +1 -1
- package/types/src/utils/variables/variableUtils.d.ts +1 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Orchestrates the config setup wizard: analyze config, prompt for missing values,
|
|
2
|
+
// and return grouped answers plus a redacted copy safe for display.
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const { runConfigWizard } = require('../ui/configWizard')
|
|
5
|
+
const { buildConfigRequirements } = require('../requirements/configRequirements')
|
|
6
|
+
const { redactUserInputsByRequirements } = require('../redaction/setupRedaction')
|
|
7
|
+
|
|
8
|
+
const ANSWER_GROUPS = /** @type {const} */ (['options', 'env', 'self', 'dotProp'])
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {Object} SetupAnswers
|
|
12
|
+
* @property {Object.<string, any>} options - answers for ${opt:...} variables
|
|
13
|
+
* @property {Object.<string, any>} env - answers for ${env:...} variables
|
|
14
|
+
* @property {Object.<string, any>} self - answers for ${self:...} variables
|
|
15
|
+
* @property {Object.<string, any>} dotProp - answers for dot-prop config references
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} SetupResult
|
|
20
|
+
* @property {number} schemaVersion - result shape version
|
|
21
|
+
* @property {string|null} configPath - absolute path to the config file, null for object input
|
|
22
|
+
* @property {Array<Object>} requirements - prompt requirements from buildConfigRequirements
|
|
23
|
+
* @property {SetupAnswers} answers - raw user answers grouped by variable type
|
|
24
|
+
* @property {SetupAnswers} redactedAnswers - answers with sensitive values redacted for display
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Ensure every answer group exists so callers get a stable shape
|
|
29
|
+
* @param {Object|undefined} userInputs - answers from the prompt renderer
|
|
30
|
+
* @returns {SetupAnswers} answers with all groups present
|
|
31
|
+
*/
|
|
32
|
+
function normalizeAnswerGroups(userInputs) {
|
|
33
|
+
/** @type {SetupAnswers} */
|
|
34
|
+
const normalized = { options: {}, env: {}, self: {}, dotProp: {} }
|
|
35
|
+
for (const group of ANSWER_GROUPS) {
|
|
36
|
+
normalized[group] = Object.assign({}, userInputs && userInputs[group])
|
|
37
|
+
}
|
|
38
|
+
return normalized
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Run the setup flow: analyze the config, prompt for unresolved values, return answers.
|
|
43
|
+
* Pure orchestration - applies nothing to process.env, config, or disk.
|
|
44
|
+
* @param {string|Object} configPathOrObject - path to config file or raw config object
|
|
45
|
+
* @param {Object} [settings] - configorama settings, plus:
|
|
46
|
+
* @param {Function} [settings.promptRenderer] - replaces the interactive wizard (mocks, answers files)
|
|
47
|
+
* @param {Object} [settings.streams] - stream overrides forwarded to the prompt renderer
|
|
48
|
+
* @param {Object} deps - injected dependencies
|
|
49
|
+
* @param {Function} [deps.analyze] - configorama.analyze, injected to avoid a require cycle
|
|
50
|
+
* @param {Object} [deps.analysis] - pre-computed analysis; skips the analyze call
|
|
51
|
+
* @returns {Promise<SetupResult>} collected answers and requirements
|
|
52
|
+
*/
|
|
53
|
+
async function runSetup(configPathOrObject, settings = {}, deps = {}) {
|
|
54
|
+
const { analyze } = deps
|
|
55
|
+
if (!deps.analysis && typeof analyze !== 'function') {
|
|
56
|
+
throw new Error('setup engine requires an analyze function or a pre-computed analysis')
|
|
57
|
+
}
|
|
58
|
+
const { promptRenderer, streams, ...analyzeSettings } = settings
|
|
59
|
+
|
|
60
|
+
const analysis = deps.analysis || await analyze(configPathOrObject, analyzeSettings)
|
|
61
|
+
const requirements = buildConfigRequirements(analysis)
|
|
62
|
+
|
|
63
|
+
const configPath = typeof configPathOrObject === 'string'
|
|
64
|
+
? path.resolve(configPathOrObject)
|
|
65
|
+
: null
|
|
66
|
+
|
|
67
|
+
const renderPrompts = promptRenderer || runConfigWizard
|
|
68
|
+
const userInputs = await renderPrompts(analysis, analysis.originalConfig || {}, configPath || '', streams)
|
|
69
|
+
|
|
70
|
+
const answers = normalizeAnswerGroups(userInputs)
|
|
71
|
+
const redactedAnswers = normalizeAnswerGroups(redactUserInputsByRequirements(answers, requirements))
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
schemaVersion: 1,
|
|
75
|
+
configPath,
|
|
76
|
+
requirements,
|
|
77
|
+
answers,
|
|
78
|
+
redactedAnswers,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
runSetup,
|
|
84
|
+
normalizeAnswerGroups,
|
|
85
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Temporarily delegates process.stdout writes to another stream so interactive
|
|
2
|
+
// prompt UI can render elsewhere (e.g. stderr) while stdout stays machine-clean.
|
|
3
|
+
|
|
4
|
+
const MIRRORED_TTY_PROPS = ['rows', 'columns', 'isTTY']
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Run fn with all process.stdout writes forwarded to target.
|
|
8
|
+
* TTY dimensions are mirrored from target so prompt layout matches the
|
|
9
|
+
* stream it actually renders on (stdout is often a pipe in --export mode).
|
|
10
|
+
* @param {import('stream').Writable} target - stream that receives the writes
|
|
11
|
+
* @param {Function} fn - async function to run while redirected
|
|
12
|
+
* @returns {Promise<any>} fn's result
|
|
13
|
+
*/
|
|
14
|
+
async function withStdoutRedirected(target, fn) {
|
|
15
|
+
const originalWrite = process.stdout.write
|
|
16
|
+
const savedDescriptors = {}
|
|
17
|
+
|
|
18
|
+
process.stdout.write = /** @type {typeof process.stdout.write} */ (
|
|
19
|
+
(chunk, encoding, callback) => target.write(chunk, encoding, callback)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
for (const prop of MIRRORED_TTY_PROPS) {
|
|
23
|
+
if (target[prop] === undefined) continue
|
|
24
|
+
savedDescriptors[prop] = Object.getOwnPropertyDescriptor(process.stdout, prop) || null
|
|
25
|
+
Object.defineProperty(process.stdout, prop, {
|
|
26
|
+
configurable: true,
|
|
27
|
+
enumerable: true,
|
|
28
|
+
get() { return target[prop] },
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
return await fn()
|
|
34
|
+
} finally {
|
|
35
|
+
process.stdout.write = originalWrite
|
|
36
|
+
for (const [prop, descriptor] of Object.entries(savedDescriptors)) {
|
|
37
|
+
if (descriptor) {
|
|
38
|
+
Object.defineProperty(process.stdout, prop, descriptor)
|
|
39
|
+
} else {
|
|
40
|
+
delete process.stdout[prop]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
withStdoutRedirected,
|
|
48
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Writes setup answers to a versioned JSON file for automation (agents, CI).
|
|
2
|
+
// Answers can contain raw secrets, so writes are 0600, atomic, and overwrite-safe.
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const { ConfigoramaError } = require('../../errors')
|
|
6
|
+
const { normalizeAnswerGroups } = require('./setupEngine')
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Write setup answers to a JSON file
|
|
10
|
+
* @param {string} filePath - target path
|
|
11
|
+
* @param {Object} answers - answer groups { options, env, self, dotProp }
|
|
12
|
+
* @param {Object} [opts] - write behavior
|
|
13
|
+
* @param {boolean} [opts.force] - overwrite an existing file
|
|
14
|
+
* @returns {{ path: string, groups: Object.<string, string[]> }} written target and key names per group
|
|
15
|
+
*/
|
|
16
|
+
function writeAnswers(filePath, answers, opts = {}) {
|
|
17
|
+
if (fs.existsSync(filePath) && !opts.force) {
|
|
18
|
+
throw new ConfigoramaError(
|
|
19
|
+
'target_file_exists',
|
|
20
|
+
`${filePath} already exists. Pass --force to overwrite.`
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const normalized = normalizeAnswerGroups(answers)
|
|
25
|
+
const payload = {
|
|
26
|
+
schemaVersion: 1,
|
|
27
|
+
answers: normalized,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const dir = path.dirname(filePath)
|
|
31
|
+
const tmpPath = path.join(dir, `.${path.basename(filePath)}.configx-tmp-${process.pid}`)
|
|
32
|
+
fs.writeFileSync(tmpPath, `${JSON.stringify(payload, null, 2)}\n`, { mode: 0o600 })
|
|
33
|
+
try {
|
|
34
|
+
fs.renameSync(tmpPath, filePath)
|
|
35
|
+
} catch (err) {
|
|
36
|
+
fs.unlinkSync(tmpPath)
|
|
37
|
+
throw err
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** @type {Object.<string, string[]>} */
|
|
41
|
+
const groups = {}
|
|
42
|
+
for (const [group, values] of Object.entries(normalized)) {
|
|
43
|
+
groups[group] = Object.keys(values)
|
|
44
|
+
}
|
|
45
|
+
return { path: filePath, groups }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
writeAnswers,
|
|
50
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Writes env answers to a dotenv file safely: strict key validation, quoting
|
|
2
|
+
// that round-trips, 0600 permissions, managed-block merge, atomic writes.
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const { ConfigoramaError } = require('../../errors')
|
|
6
|
+
|
|
7
|
+
const MANAGED_BLOCK_START = '# >>> configx setup values >>>'
|
|
8
|
+
const MANAGED_BLOCK_END = '# <<< configx setup values <<<'
|
|
9
|
+
|
|
10
|
+
const VALID_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/
|
|
11
|
+
const PLAIN_VALUE = /^[A-Za-z0-9_./:@+=-]*$/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Quote a value so common dotenv parsers read it back verbatim
|
|
15
|
+
* @param {string} value - raw value
|
|
16
|
+
* @returns {string} dotenv-safe representation
|
|
17
|
+
*/
|
|
18
|
+
function quoteDotenvValue(value) {
|
|
19
|
+
const str = String(value)
|
|
20
|
+
if (PLAIN_VALUE.test(str)) return str
|
|
21
|
+
// Single quotes are literal in dotenv - no expansion of $ or escapes
|
|
22
|
+
if (!str.includes("'") && !str.includes('\n')) return `'${str}'`
|
|
23
|
+
const escaped = str
|
|
24
|
+
.replace(/\\/g, '\\\\')
|
|
25
|
+
.replace(/"/g, '\\"')
|
|
26
|
+
.replace(/\n/g, '\\n')
|
|
27
|
+
return `"${escaped}"`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Render KEY=value lines for the given values
|
|
32
|
+
* @param {Object.<string, any>} values - env key/value pairs
|
|
33
|
+
* @returns {string[]} dotenv lines
|
|
34
|
+
*/
|
|
35
|
+
function renderLines(values) {
|
|
36
|
+
return Object.entries(values).map(([key, value]) => `${key}=${quoteDotenvValue(value)}`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Replace or append the managed block in existing dotenv content
|
|
41
|
+
* @param {string} content - current file content
|
|
42
|
+
* @param {string[]} lines - new KEY=value lines for the block
|
|
43
|
+
* @param {string} filePath - target path for error messages
|
|
44
|
+
* @returns {string} updated file content
|
|
45
|
+
*/
|
|
46
|
+
function mergeManagedBlock(content, lines, filePath) {
|
|
47
|
+
const startCount = content.split(MANAGED_BLOCK_START).length - 1
|
|
48
|
+
const endCount = content.split(MANAGED_BLOCK_END).length - 1
|
|
49
|
+
|
|
50
|
+
if (startCount > 1 || endCount > 1 || startCount !== endCount) {
|
|
51
|
+
throw new ConfigoramaError(
|
|
52
|
+
'managed_block_invalid',
|
|
53
|
+
`Cannot merge into ${filePath}: expected exactly one intact managed block, found ${startCount} start and ${endCount} end marker(s).`
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const block = [MANAGED_BLOCK_START, ...lines, MANAGED_BLOCK_END].join('\n')
|
|
58
|
+
|
|
59
|
+
if (startCount === 0) {
|
|
60
|
+
const separator = content.length === 0 || content.endsWith('\n') ? '' : '\n'
|
|
61
|
+
return `${content}${separator}${block}\n`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const startIndex = content.indexOf(MANAGED_BLOCK_START)
|
|
65
|
+
const endIndex = content.indexOf(MANAGED_BLOCK_END)
|
|
66
|
+
if (endIndex < startIndex) {
|
|
67
|
+
throw new ConfigoramaError(
|
|
68
|
+
'managed_block_invalid',
|
|
69
|
+
`Cannot merge into ${filePath}: managed block markers are out of order.`
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
return content.slice(0, startIndex) + block + content.slice(endIndex + MANAGED_BLOCK_END.length)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Write values atomically: temp file in the same directory, then rename
|
|
77
|
+
* @param {string} filePath - target path
|
|
78
|
+
* @param {string} content - file content
|
|
79
|
+
* @param {number} mode - file mode for new files
|
|
80
|
+
*/
|
|
81
|
+
function atomicWrite(filePath, content, mode) {
|
|
82
|
+
const dir = path.dirname(filePath)
|
|
83
|
+
const tmpPath = path.join(dir, `.${path.basename(filePath)}.configx-tmp-${process.pid}`)
|
|
84
|
+
fs.writeFileSync(tmpPath, content, { mode })
|
|
85
|
+
try {
|
|
86
|
+
fs.renameSync(tmpPath, filePath)
|
|
87
|
+
} catch (err) {
|
|
88
|
+
fs.unlinkSync(tmpPath)
|
|
89
|
+
throw err
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Write env answers to a dotenv file
|
|
95
|
+
* @param {string} filePath - target dotenv path
|
|
96
|
+
* @param {Object.<string, any>} values - env key/value pairs to write
|
|
97
|
+
* @param {Object} [opts] - write behavior
|
|
98
|
+
* @param {boolean} [opts.merge] - replace/append the managed block, preserving other content
|
|
99
|
+
* @param {boolean} [opts.force] - overwrite the whole file if it exists
|
|
100
|
+
* @returns {{ path: string, keys: string[] }} written target and key names for summaries
|
|
101
|
+
*/
|
|
102
|
+
function writeDotenv(filePath, values, opts = {}) {
|
|
103
|
+
const keys = Object.keys(values || {})
|
|
104
|
+
for (const key of keys) {
|
|
105
|
+
if (!VALID_KEY.test(key)) {
|
|
106
|
+
throw new ConfigoramaError(
|
|
107
|
+
'invalid_env_key',
|
|
108
|
+
`Cannot write "${key}" to ${filePath}: dotenv keys must match ${VALID_KEY}.`
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const lines = renderLines(values)
|
|
114
|
+
const exists = fs.existsSync(filePath)
|
|
115
|
+
|
|
116
|
+
if (exists && !opts.merge && !opts.force) {
|
|
117
|
+
throw new ConfigoramaError(
|
|
118
|
+
'target_file_exists',
|
|
119
|
+
`${filePath} already exists. Pass --merge to update the managed block or --force to overwrite.`
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let content
|
|
124
|
+
let mode = 0o600
|
|
125
|
+
if (opts.merge) {
|
|
126
|
+
const existingContent = exists ? fs.readFileSync(filePath, 'utf8') : ''
|
|
127
|
+
if (exists) mode = fs.statSync(filePath).mode & 0o777
|
|
128
|
+
content = mergeManagedBlock(existingContent, lines, filePath)
|
|
129
|
+
} else {
|
|
130
|
+
content = `${lines.join('\n')}\n`
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
atomicWrite(filePath, content, mode)
|
|
134
|
+
return { path: filePath, keys }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = {
|
|
138
|
+
writeDotenv,
|
|
139
|
+
quoteDotenvValue,
|
|
140
|
+
MANAGED_BLOCK_START,
|
|
141
|
+
MANAGED_BLOCK_END,
|
|
142
|
+
}
|
|
@@ -8,6 +8,7 @@ const { toClickablePath } = require('./createEditorLink')
|
|
|
8
8
|
const { buildConfigRequirements } = require('../requirements/configRequirements')
|
|
9
9
|
const { createPromptDescriptors } = require('./promptDescriptors')
|
|
10
10
|
const { isSensitiveVariable } = require('../redaction/redact')
|
|
11
|
+
const { withStdoutRedirected } = require('../setup/stdoutRedirect')
|
|
11
12
|
|
|
12
13
|
const INVISIBLE_SPACE = '\u2800\u2800\u2800'
|
|
13
14
|
|
|
@@ -613,9 +614,27 @@ function createPromptMessage(varInfo) {
|
|
|
613
614
|
* Runs config setup wizard
|
|
614
615
|
* @param {object} metadata - Enriched metadata from configorama
|
|
615
616
|
* @param {object} originalConfig - The original config before resolution
|
|
617
|
+
* @param {string} [configFilePath] - Path to the config file for display
|
|
618
|
+
* @param {object} [streams] - Stream overrides for the prompt UI
|
|
619
|
+
* @param {import('stream').Writable} [streams.output] - Where prompt UI renders; stdout stays clean when set
|
|
616
620
|
* @returns {Promise<object>} User inputs by variable type
|
|
617
621
|
*/
|
|
618
|
-
async function runConfigWizard(metadata, originalConfig = {}, configFilePath = '') {
|
|
622
|
+
async function runConfigWizard(metadata, originalConfig = {}, configFilePath = '', streams) {
|
|
623
|
+
const output = streams && streams.output
|
|
624
|
+
if (output && output !== process.stdout) {
|
|
625
|
+
return withStdoutRedirected(output, () => renderConfigWizard(metadata, originalConfig, configFilePath))
|
|
626
|
+
}
|
|
627
|
+
return renderConfigWizard(metadata, originalConfig, configFilePath)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Renders the wizard prompts on process.stdout
|
|
632
|
+
* @param {object} metadata - Enriched metadata from configorama
|
|
633
|
+
* @param {object} originalConfig - The original config before resolution
|
|
634
|
+
* @param {string} [configFilePath] - Path to the config file for display
|
|
635
|
+
* @returns {Promise<object>} User inputs by variable type
|
|
636
|
+
*/
|
|
637
|
+
async function renderConfigWizard(metadata, originalConfig = {}, configFilePath = '') {
|
|
619
638
|
const { uniqueVariables } = metadata
|
|
620
639
|
|
|
621
640
|
if (!uniqueVariables || Object.keys(uniqueVariables).length === 0) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const SCHEMA_VERSION: 1;
|
|
2
|
+
export const VIEWS: string[];
|
|
3
|
+
/**
|
|
4
|
+
* Build the capabilities contract object.
|
|
5
|
+
* @returns {object} stable, deterministic description of the CLI surface
|
|
6
|
+
*/
|
|
7
|
+
export function buildCapabilities(): object;
|
|
8
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/capabilities.js"],"names":[],"mappings":"AAOA,6BAAuB,CAAC,CAAA;AAExB,6BAAgD;AAEhD;;;GAGG;AACH,qCAFa,MAAM,CA2ClB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display "No Variables Found" message
|
|
3
|
+
* @param {string} configFilePath
|
|
4
|
+
* @param {RegExp} variableSyntax
|
|
5
|
+
* @param {Object} variableTypes
|
|
6
|
+
*/
|
|
7
|
+
export function displayNoVariablesFound(configFilePath: string, variableSyntax: RegExp, variableTypes: any): void;
|
|
8
|
+
/**
|
|
9
|
+
* Display variable details in stacked box format
|
|
10
|
+
* @param {Object} params
|
|
11
|
+
* @param {string[]} params.varKeys
|
|
12
|
+
* @param {Object} params.variableData
|
|
13
|
+
* @param {Object} params.uniqueVariables
|
|
14
|
+
* @param {RegExp} params.varPrefixPattern
|
|
15
|
+
* @param {RegExp} params.varSuffixPattern
|
|
16
|
+
* @param {string[]} params.lines
|
|
17
|
+
* @param {string} params.fileType
|
|
18
|
+
* @param {string} params.configFilePath
|
|
19
|
+
*/
|
|
20
|
+
export function displayVariableDetails({ varKeys, variableData, uniqueVariables, varPrefixPattern, varSuffixPattern, lines, fileType, configFilePath }: {
|
|
21
|
+
varKeys: string[];
|
|
22
|
+
variableData: any;
|
|
23
|
+
uniqueVariables: any;
|
|
24
|
+
varPrefixPattern: RegExp;
|
|
25
|
+
varSuffixPattern: RegExp;
|
|
26
|
+
lines: string[];
|
|
27
|
+
fileType: string;
|
|
28
|
+
configFilePath: string;
|
|
29
|
+
}): void;
|
|
30
|
+
/**
|
|
31
|
+
* Display unique variables in stacked box format
|
|
32
|
+
* @param {Object} params
|
|
33
|
+
* @param {string[]} params.uniqueVarKeys
|
|
34
|
+
* @param {Object} params.uniqueVariables
|
|
35
|
+
* @param {string[]} params.lines
|
|
36
|
+
* @param {string} params.fileType
|
|
37
|
+
* @param {string} params.configFilePath
|
|
38
|
+
*/
|
|
39
|
+
export function displayUniqueVariables({ uniqueVarKeys, uniqueVariables, lines, fileType, configFilePath }: {
|
|
40
|
+
uniqueVarKeys: string[];
|
|
41
|
+
uniqueVariables: any;
|
|
42
|
+
lines: string[];
|
|
43
|
+
fileType: string;
|
|
44
|
+
configFilePath: string;
|
|
45
|
+
}): void;
|
|
46
|
+
/**
|
|
47
|
+
* Display configurable variables grouped by source type
|
|
48
|
+
* @param {Object} params
|
|
49
|
+
* @param {string[]} params.uniqueVarKeys
|
|
50
|
+
* @param {Object} params.uniqueVariables
|
|
51
|
+
* @param {string[]} params.lines
|
|
52
|
+
* @param {string} params.fileType
|
|
53
|
+
* @param {string} params.configFilePath
|
|
54
|
+
*/
|
|
55
|
+
export function displayConfigurableVariables({ uniqueVarKeys, uniqueVariables, lines, fileType, configFilePath }: {
|
|
56
|
+
uniqueVarKeys: string[];
|
|
57
|
+
uniqueVariables: any;
|
|
58
|
+
lines: string[];
|
|
59
|
+
fileType: string;
|
|
60
|
+
configFilePath: string;
|
|
61
|
+
}): void;
|
|
62
|
+
//# sourceMappingURL=display.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../../src/display.js"],"names":[],"mappings":"AAiEA;;;;;GAKG;AACH,wDAJW,MAAM,kBACN,MAAM,4BAwBhB;AAED;;;;;;;;;;;GAWG;AACH,wJATG;IAAyB,OAAO,EAAxB,MAAM,EAAE;IACO,YAAY;IACZ,eAAe;IACf,gBAAgB,EAA/B,MAAM;IACS,gBAAgB,EAA/B,MAAM;IACW,KAAK,EAAtB,MAAM,EAAE;IACO,QAAQ,EAAvB,MAAM;IACS,cAAc,EAA7B,MAAM;CAChB,QAiKA;AAED;;;;;;;;GAQG;AACH,4GANG;IAAyB,aAAa,EAA9B,MAAM,EAAE;IACO,eAAe;IACb,KAAK,EAAtB,MAAM,EAAE;IACO,QAAQ,EAAvB,MAAM;IACS,cAAc,EAA7B,MAAM;CAChB,QAmHA;AAED;;;;;;;;GAQG;AACH,kHANG;IAAyB,aAAa,EAA9B,MAAM,EAAE;IACO,eAAe;IACb,KAAK,EAAtB,MAAM,EAAE;IACO,QAAQ,EAAvB,MAAM;IACS,cAAc,EAA7B,MAAM;CAChB,QA2IA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const ERROR_CODES: {
|
|
2
|
+
code: string;
|
|
3
|
+
description: string;
|
|
4
|
+
}[];
|
|
5
|
+
export class ConfigoramaError extends Error {
|
|
6
|
+
constructor(code: any, message: any, details?: {});
|
|
7
|
+
code: any;
|
|
8
|
+
details: {};
|
|
9
|
+
toJSON(): {
|
|
10
|
+
error: {
|
|
11
|
+
code: any;
|
|
12
|
+
message: string;
|
|
13
|
+
details: {};
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function classifyErrorMessage(message: any, fallbackCode?: string): string;
|
|
18
|
+
export function isConfigoramaError(error: any): boolean;
|
|
19
|
+
export function normalizeError(error: any, fallbackCode?: string): any;
|
|
20
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.js"],"names":[],"mappings":"AAGA;;;IAmBC;AAED;IACE,mDAKC;IAFC,UAAuC;IACvC,YAAsB;IAGxB;;;;;;MAQC;CACF;AAgBD,kFAUC;AAxBD,wDAEC;AAED,uEAQC"}
|
package/types/src/index.d.ts
CHANGED
|
@@ -5,6 +5,99 @@ declare function _exports<T = any>(configPathOrObject: string | any, settings?:
|
|
|
5
5
|
declare namespace _exports {
|
|
6
6
|
export function sync<T = any>(configPathOrObject: string | any, settings?: ConfigoramaSettings): T;
|
|
7
7
|
export function analyze(configPathOrObject: string | object, settings?: object): Promise<any>;
|
|
8
|
+
export function setup(configPathOrObject: string | any, settings?: object): Promise<object>;
|
|
9
|
+
export { writeDotenv };
|
|
10
|
+
export { writeAnswers };
|
|
11
|
+
export function introspect(configPathOrObject: any, settings?: {}): Promise<{
|
|
12
|
+
schemaVersion: number;
|
|
13
|
+
nodes: ({
|
|
14
|
+
id: string;
|
|
15
|
+
kind: string;
|
|
16
|
+
variable: any;
|
|
17
|
+
variableType: any;
|
|
18
|
+
sourceClass: any;
|
|
19
|
+
risk: string;
|
|
20
|
+
severity: string;
|
|
21
|
+
paths: any[];
|
|
22
|
+
sensitive: boolean;
|
|
23
|
+
} | {
|
|
24
|
+
id: string;
|
|
25
|
+
kind: string;
|
|
26
|
+
path: any;
|
|
27
|
+
relativePath: any;
|
|
28
|
+
exists: any;
|
|
29
|
+
risk: string;
|
|
30
|
+
severity: string;
|
|
31
|
+
})[];
|
|
32
|
+
edges: {
|
|
33
|
+
from: string;
|
|
34
|
+
to: string;
|
|
35
|
+
kind: string;
|
|
36
|
+
}[];
|
|
37
|
+
diagnostics: {
|
|
38
|
+
code: string;
|
|
39
|
+
severity: string;
|
|
40
|
+
variable: any;
|
|
41
|
+
message: string;
|
|
42
|
+
}[];
|
|
43
|
+
summary: {
|
|
44
|
+
nodes: number;
|
|
45
|
+
edges: number;
|
|
46
|
+
diagnostics: number;
|
|
47
|
+
};
|
|
48
|
+
}>;
|
|
49
|
+
export function audit(configPathOrObject: any, settings?: {}): Promise<{
|
|
50
|
+
schemaVersion: number;
|
|
51
|
+
safeMode: boolean;
|
|
52
|
+
findings: any[];
|
|
53
|
+
diagnostics: any;
|
|
54
|
+
summary: {
|
|
55
|
+
high: number;
|
|
56
|
+
medium: number;
|
|
57
|
+
low: number;
|
|
58
|
+
info: number;
|
|
59
|
+
total: number;
|
|
60
|
+
};
|
|
61
|
+
}>;
|
|
62
|
+
export function graph(configPathOrObject: any, settings?: {}): Promise<string | {
|
|
63
|
+
schemaVersion: number;
|
|
64
|
+
nodes: ({
|
|
65
|
+
id: string;
|
|
66
|
+
kind: string;
|
|
67
|
+
variable: any;
|
|
68
|
+
variableType: any;
|
|
69
|
+
sourceClass: any;
|
|
70
|
+
risk: string;
|
|
71
|
+
severity: string;
|
|
72
|
+
paths: any[];
|
|
73
|
+
sensitive: boolean;
|
|
74
|
+
} | {
|
|
75
|
+
id: string;
|
|
76
|
+
kind: string;
|
|
77
|
+
path: any;
|
|
78
|
+
relativePath: any;
|
|
79
|
+
exists: any;
|
|
80
|
+
risk: string;
|
|
81
|
+
severity: string;
|
|
82
|
+
})[];
|
|
83
|
+
edges: {
|
|
84
|
+
from: string;
|
|
85
|
+
to: string;
|
|
86
|
+
kind: string;
|
|
87
|
+
}[];
|
|
88
|
+
diagnostics: {
|
|
89
|
+
code: string;
|
|
90
|
+
severity: string;
|
|
91
|
+
variable: any;
|
|
92
|
+
message: string;
|
|
93
|
+
}[];
|
|
94
|
+
summary: {
|
|
95
|
+
nodes: number;
|
|
96
|
+
edges: number;
|
|
97
|
+
diagnostics: number;
|
|
98
|
+
};
|
|
99
|
+
}>;
|
|
100
|
+
export function inspect(configPathOrObject: string | object, settings?: object): Promise<object | string>;
|
|
8
101
|
export { parsers as format };
|
|
9
102
|
export { Configorama };
|
|
10
103
|
export { buildVariableSyntax };
|
|
@@ -49,6 +142,18 @@ type ConfigoramaSettings = {
|
|
|
49
142
|
* - allow undefined values to pass through without throwing errors
|
|
50
143
|
*/
|
|
51
144
|
allowUndefinedValues?: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* - glob-like config paths whose values should be left verbatim
|
|
147
|
+
*/
|
|
148
|
+
ignorePaths?: string[];
|
|
149
|
+
/**
|
|
150
|
+
* - alias for ignorePaths
|
|
151
|
+
*/
|
|
152
|
+
skipResolutionPaths?: string[];
|
|
153
|
+
/**
|
|
154
|
+
* - disable built-in CloudFormation and embedded-code ignore paths
|
|
155
|
+
*/
|
|
156
|
+
disableDefaultIgnorePaths?: boolean;
|
|
52
157
|
/**
|
|
53
158
|
* - values passed into .js config files if user using javascript config
|
|
54
159
|
*/
|
|
@@ -57,6 +162,14 @@ type ConfigoramaSettings = {
|
|
|
57
162
|
* - return both config and metadata about variables found
|
|
58
163
|
*/
|
|
59
164
|
returnMetadata?: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* - suppress env-stage-loader logs when useDotenv/useDotEnv is enabled
|
|
167
|
+
*/
|
|
168
|
+
dotEnvSilent?: boolean;
|
|
169
|
+
/**
|
|
170
|
+
* - enable env-stage-loader debug logs when useDotenv/useDotEnv is enabled
|
|
171
|
+
*/
|
|
172
|
+
dotEnvDebug?: boolean;
|
|
60
173
|
/**
|
|
61
174
|
* - keys to merge in arrays of objects
|
|
62
175
|
*/
|
|
@@ -67,6 +180,10 @@ type ConfigoramaSettings = {
|
|
|
67
180
|
filePathOverrides?: {
|
|
68
181
|
[x: string]: string;
|
|
69
182
|
};
|
|
183
|
+
/**
|
|
184
|
+
* - install Configorama CLI signal handlers. Defaults to false for library calls.
|
|
185
|
+
*/
|
|
186
|
+
handleSignalEvents?: boolean;
|
|
70
187
|
};
|
|
71
188
|
type ConfigoramaResult<T = any> = {
|
|
72
189
|
/**
|
|
@@ -96,6 +213,8 @@ type ConfigoramaResult<T = any> = {
|
|
|
96
213
|
*/
|
|
97
214
|
resolutionHistory: any;
|
|
98
215
|
};
|
|
216
|
+
import { writeDotenv } from "./utils/setup/writeDotenv";
|
|
217
|
+
import { writeAnswers } from "./utils/setup/writeAnswers";
|
|
99
218
|
import parsers = require("./parsers");
|
|
100
219
|
import Configorama = require("./main");
|
|
101
220
|
import { buildVariableSyntax } from "./utils/variables/variableUtils";
|
package/types/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.js"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.js"],"names":[],"mappings":";;;AAwDiB,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,gBAchB;IAQsB,0CAJZ,MAAM,MAAO,aACb,MAAM,GACL,OAAO,CAAC,MAAM,CAAC,CAI1B;;;IAQ2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAS3B;IAEsB;;;;;;;;;;;;OAyBtB;IAEsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAItB;IAUwB,4CAJd,MAAM,GAAC,MAAM,aACb,MAAM,GACL,OAAO,CAAC,MAAM,GAAC,MAAM,CAAC,CAiCjC;;;;;;;;;;;;;;;;aAnOa,MAAM;;;;gBACN,MAAM;;;;;;;;;;;;;;;;;;;;uBAIN,OAAO;;;;2BACP,OAAO;;;;kBACP,MAAM,EAAE;;;;0BACR,MAAM,EAAE;;;;gCACR,OAAO;;;;kBACP,cAAe;;;;qBACf,OAAO;;;;mBACP,OAAO;;;;kBACP,OAAO;;;;gBACP,MAAM,EAAE;;;;;;;;;;yBAER,OAAO;;uBAIP,CAAC;;;;oBAED,MAAM;;;;;;;;;;YAEN,CAAC"}
|