@dotenvx/dotenvx 2.9.0 → 2.11.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/CHANGELOG.md +18 -2
- package/README.md +33 -0
- package/package.json +1 -1
- package/src/cli/actions/run.js +12 -4
- package/src/cli/actions/update.js +15 -0
- package/src/cli/dotenvx.js +8 -0
- package/src/lib/helpers/catchAndLog.js +0 -5
- package/src/lib/helpers/colorDepth.js +7 -1
- package/src/lib/helpers/decryptedValues.js +32 -0
- package/src/lib/helpers/executeCommand.js +20 -3
- package/src/lib/helpers/fsx.js +19 -2
- package/src/lib/helpers/redact.js +9 -0
- package/src/lib/helpers/redactOutput.js +120 -0
- package/src/lib/main.d.ts +0 -1
- package/src/shared/colors.js +4 -4
- package/src/shared/logger.js +31 -35
package/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
-
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.11.0...main)
|
|
6
|
+
|
|
7
|
+
## [2.11.0](https://github.com/dotenvx/dotenvx/compare/v2.10.0...v2.11.0) (2026-07-15)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* Add convenience method `dotenvx update` ([#897](https://github.com/dotenvx/dotenvx/pull/897))
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
* BREAKING: Send informational messages like `injected env` to stderr instead of stdout. This makes it more reliable to do things like piping output to a file without needing `--quiet`. ([#898](https://github.com/dotenvx/dotenvx/pull/898))
|
|
16
|
+
|
|
17
|
+
## [2.10.0](https://github.com/dotenvx/dotenvx/compare/v2.9.0...v2.10.0) (2026-07-15)
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
* Add `run --redact` to keep secrets out of command output. ([#894](https://github.com/dotenvx/dotenvx/pull/894))
|
|
6
22
|
|
|
7
23
|
## [2.9.0](https://github.com/dotenvx/dotenvx/compare/v2.8.0...v2.9.0) (2026-07-14)
|
|
8
24
|
|
|
9
25
|
### Changed
|
|
10
26
|
|
|
11
|
-
* BREAKING: `ls` from `@dotenvx/dotenvx` is now async/await.
|
|
27
|
+
* BREAKING: `ls` from `@dotenvx/dotenvx` is now async/await. ([#893](https://github.com/dotenvx/dotenvx/pull/893))
|
|
12
28
|
|
|
13
29
|
## [2.8.0](https://github.com/dotenvx/dotenvx/compare/v2.7.3...v2.8.0) (2026-07-14)
|
|
14
30
|
|
package/README.md
CHANGED
|
@@ -1471,6 +1471,24 @@ $ dotenvx set HELLO Dotenvx -fk .env.keys -f apps/app1/.env
|
|
|
1471
1471
|
$ dotenvx run -fk .env.keys -f apps/app1/.env -- yourcommand
|
|
1472
1472
|
```
|
|
1473
1473
|
|
|
1474
|
+
</details>
|
|
1475
|
+
<details><summary>`run --redact`</summary><br>
|
|
1476
|
+
|
|
1477
|
+
Redact successfully decrypted values from the command's stdout and stderr. The command still receives the real values; only its output is filtered.
|
|
1478
|
+
|
|
1479
|
+
```sh
|
|
1480
|
+
$ touch .env
|
|
1481
|
+
$ dotenvx set SECRET super-secret-value
|
|
1482
|
+
$ echo "console.log(process.env.SECRET)" > index.js
|
|
1483
|
+
|
|
1484
|
+
$ dotenvx run --redact --quiet -- node index.js
|
|
1485
|
+
[REDACTED]
|
|
1486
|
+
```
|
|
1487
|
+
|
|
1488
|
+
Redaction is off by default. It applies only to values that were encrypted, successfully decrypted, and injected. Plaintext values are left unchanged. Matching is exact, so transformed or derived values are not redacted.
|
|
1489
|
+
|
|
1490
|
+
Because redaction filters stdout and stderr, interactive commands that require a TTY may behave differently.
|
|
1491
|
+
|
|
1474
1492
|
</details>
|
|
1475
1493
|
<details><summary>`run --mask`</summary><br>
|
|
1476
1494
|
|
|
@@ -2335,6 +2353,21 @@ $ dotenvx ls -ef '**/.env.prod*'
|
|
|
2335
2353
|
└─ .env
|
|
2336
2354
|
```
|
|
2337
2355
|
|
|
2356
|
+
</details>
|
|
2357
|
+
<details><summary>`ls --json`</summary><br>
|
|
2358
|
+
|
|
2359
|
+
Print all matching `.env` files as a JSON array of absolute filepaths. Progress and summary details are written to stderr, so stdout can be safely piped to another command or file.
|
|
2360
|
+
|
|
2361
|
+
```sh
|
|
2362
|
+
$ dotenvx ls --json
|
|
2363
|
+
[
|
|
2364
|
+
"/path/to/project/.env",
|
|
2365
|
+
"/path/to/project/apps/backend/.env"
|
|
2366
|
+
]
|
|
2367
|
+
|
|
2368
|
+
$ dotenvx ls --json > dotenv-files.json
|
|
2369
|
+
```
|
|
2370
|
+
|
|
2338
2371
|
</details>
|
|
2339
2372
|
<details><summary>`genexample`</summary><br>
|
|
2340
2373
|
|
package/package.json
CHANGED
package/src/cli/actions/run.js
CHANGED
|
@@ -13,6 +13,8 @@ const resolveEnvKeysFile = require('../../lib/helpers/resolveEnvKeysFile')
|
|
|
13
13
|
const mask = require('../../lib/helpers/mask')
|
|
14
14
|
const maskEnvSrc = require('../../lib/helpers/maskEnvSrc')
|
|
15
15
|
const maskProcessedEnvs = require('../../lib/helpers/maskProcessedEnvs')
|
|
16
|
+
const decryptedValues = require('../../lib/helpers/decryptedValues')
|
|
17
|
+
const { redactOutput } = require('../../lib/helpers/redactOutput')
|
|
16
18
|
|
|
17
19
|
const { determine } = require('./../../lib/helpers/envResolution')
|
|
18
20
|
|
|
@@ -53,11 +55,13 @@ async function run () {
|
|
|
53
55
|
const options = normalizeDotenvConfigConvention(normalizeDotenvConfigQuiet(this.opts()))
|
|
54
56
|
const spinnerOptions = typeof this.optsWithGlobals === 'function' ? this.optsWithGlobals() : options
|
|
55
57
|
const maskEnabled = options.mask !== undefined
|
|
58
|
+
const redactEnabled = options.redact === true
|
|
56
59
|
let showChar = options.mask
|
|
57
60
|
if (options.mask === true) {
|
|
58
61
|
showChar = 6
|
|
59
62
|
}
|
|
60
63
|
let commandEnv = process.env
|
|
64
|
+
let sensitiveValues = []
|
|
61
65
|
|
|
62
66
|
let commandArgs = this.args
|
|
63
67
|
if (commandArgs.length < 1) {
|
|
@@ -122,6 +126,10 @@ async function run () {
|
|
|
122
126
|
}
|
|
123
127
|
})
|
|
124
128
|
|
|
129
|
+
if (redactEnabled) {
|
|
130
|
+
sensitiveValues = decryptedValues(processedEnvs)
|
|
131
|
+
}
|
|
132
|
+
|
|
125
133
|
if (maskEnabled) {
|
|
126
134
|
commandEnv = { ...process.env }
|
|
127
135
|
maskProcessedEnvs(processedEnvs, commandEnv, showChar)
|
|
@@ -156,18 +164,18 @@ async function run () {
|
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
// debug parsed
|
|
159
|
-
logger.debug(processedEnv.parsed)
|
|
167
|
+
logger.debug(redactOutput(processedEnv.parsed, sensitiveValues))
|
|
160
168
|
|
|
161
169
|
// verbose/debug injected key/value
|
|
162
170
|
for (const [key, value] of Object.entries(processedEnv.injected || {})) {
|
|
163
171
|
logger.verbose(`${key} set`)
|
|
164
|
-
logger.debug(`${key} set to ${value}
|
|
172
|
+
logger.debug(redactOutput(`${key} set to ${value}`, sensitiveValues))
|
|
165
173
|
}
|
|
166
174
|
|
|
167
175
|
// verbose/debug existed key/value
|
|
168
176
|
for (const [key, value] of Object.entries(processedEnv.existed || {})) {
|
|
169
177
|
logger.verbose(`${key} pre-exists (protip: use --overload to override)`)
|
|
170
|
-
logger.debug(`${key} pre-exists as ${value} (protip: use --overload to override)
|
|
178
|
+
logger.debug(redactOutput(`${key} pre-exists as ${value} (protip: use --overload to override)`, sensitiveValues))
|
|
171
179
|
}
|
|
172
180
|
}
|
|
173
181
|
|
|
@@ -189,7 +197,7 @@ async function run () {
|
|
|
189
197
|
process.exit(1)
|
|
190
198
|
}
|
|
191
199
|
|
|
192
|
-
await executeCommand(commandArgs, commandEnv)
|
|
200
|
+
await executeCommand(commandArgs, commandEnv, sensitiveValues)
|
|
193
201
|
}
|
|
194
202
|
|
|
195
203
|
module.exports = run
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const childProcess = require('child_process')
|
|
2
|
+
|
|
3
|
+
const INSTALL_COMMAND = 'curl -sfS https://dotenvx.sh | sh'
|
|
4
|
+
|
|
5
|
+
function update () {
|
|
6
|
+
if (process.pkg) {
|
|
7
|
+
childProcess.execSync(INSTALL_COMMAND, { stdio: 'inherit' })
|
|
8
|
+
return
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
console.log('global: npm install -g @dotenvx/dotenvx@latest')
|
|
12
|
+
console.log('local: npm install @dotenvx/dotenvx@latest --save')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = update
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -70,6 +70,7 @@ program.command('run')
|
|
|
70
70
|
.option('--ignore <errorCodes...>', 'error code(s) to ignore (example: --ignore=MISSING_ENV_FILE)')
|
|
71
71
|
.option('--token <token>', 'set Armor ⛨ token')
|
|
72
72
|
.option('--mask [characters]', 'inject masked values, optionally setting visible characters')
|
|
73
|
+
.option('--redact', 'redact decrypted values from command output', false)
|
|
73
74
|
.option('--no-armor', 'disable Dotenvx Armor features')
|
|
74
75
|
.option('--no-native', 'disable OS secret store features')
|
|
75
76
|
.action(function (...args) {
|
|
@@ -226,6 +227,13 @@ program.command('doctor', { hidden: true })
|
|
|
226
227
|
return require('./actions/doctor').apply(this, args)
|
|
227
228
|
})
|
|
228
229
|
|
|
230
|
+
// dotenvx update
|
|
231
|
+
program.command('update')
|
|
232
|
+
.description('update dotenvx')
|
|
233
|
+
.action(function (...args) {
|
|
234
|
+
return require('./actions/update').apply(this, args)
|
|
235
|
+
})
|
|
236
|
+
|
|
229
237
|
// dotenvx login (compatibility alias for dotenvx armor login)
|
|
230
238
|
program.command('login', { hidden: true })
|
|
231
239
|
.description('log in to move keys off-device, share with your team, and audit access')
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
const { logger } = require('./../../shared/logger')
|
|
2
|
-
const Errors = require('./errors')
|
|
3
2
|
|
|
4
3
|
function catchAndLog (error) {
|
|
5
|
-
if (error.code === 'EACCES' || error.code === 'EPERM') {
|
|
6
|
-
error = new Errors({ filepath: error.path }).fileNotWritable()
|
|
7
|
-
}
|
|
8
|
-
|
|
9
4
|
const msg = error.messageWithHelp || error.message
|
|
10
5
|
if (msg) {
|
|
11
6
|
logger.error(msg)
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
const { WriteStream } = require('tty')
|
|
2
2
|
|
|
3
|
-
const getColorDepth = () => {
|
|
3
|
+
const getColorDepth = (stream) => {
|
|
4
|
+
if (stream && !stream.isTTY) return 1
|
|
5
|
+
|
|
4
6
|
try {
|
|
7
|
+
if (stream && typeof stream.getColorDepth === 'function') {
|
|
8
|
+
return stream.getColorDepth()
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
return WriteStream.prototype.getColorDepth()
|
|
6
12
|
} catch (error) {
|
|
7
13
|
const term = process.env.TERM
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { encrypted, scan } = require('@dotenvx/primitives')
|
|
2
|
+
|
|
3
|
+
function decryptedValues (processedEnvs) {
|
|
4
|
+
const result = new Set()
|
|
5
|
+
|
|
6
|
+
for (const processedEnv of processedEnvs || []) {
|
|
7
|
+
const src = processedEnv.src || processedEnv.string
|
|
8
|
+
if (!src) continue
|
|
9
|
+
|
|
10
|
+
let rawParsed
|
|
11
|
+
try {
|
|
12
|
+
rawParsed = scan(src).parsed
|
|
13
|
+
} catch (error) {
|
|
14
|
+
continue
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
for (const [key, rawValues] of Object.entries(rawParsed || {})) {
|
|
18
|
+
const rawValue = rawValues[rawValues.length - 1]
|
|
19
|
+
const injectedValue = (processedEnv.injected || {})[key]
|
|
20
|
+
|
|
21
|
+
if (!encrypted(rawValue)) continue
|
|
22
|
+
if (injectedValue === undefined || injectedValue === null || injectedValue === '') continue
|
|
23
|
+
if (encrypted(injectedValue)) continue
|
|
24
|
+
|
|
25
|
+
result.add(`${injectedValue}`)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return [...result]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = decryptedValues
|
|
@@ -3,8 +3,9 @@ const { which } = require('@dotenvx/tooling')
|
|
|
3
3
|
const execute = require('./../../lib/helpers/execute')
|
|
4
4
|
const { logger } = require('./../../shared/logger')
|
|
5
5
|
const Errors = require('./errors')
|
|
6
|
+
const { createRedactedStreamWriter, redactOutput } = require('./redactOutput')
|
|
6
7
|
|
|
7
|
-
async function executeCommand (commandArgs, env) {
|
|
8
|
+
async function executeCommand (commandArgs, env, sensitiveValues = []) {
|
|
8
9
|
const FORWARD_SIGNAL_GRACE_MS = 1000
|
|
9
10
|
const FORCE_KILL_GRACE_MS = 1000
|
|
10
11
|
const signals = [
|
|
@@ -130,11 +131,27 @@ async function executeCommand (commandArgs, env) {
|
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
|
|
134
|
+
const redactStdout = sensitiveValues.length > 0
|
|
135
|
+
const redactStderr = sensitiveValues.length > 0
|
|
136
|
+
|
|
133
137
|
child = execute.execa(commandArgs[0], commandArgs.slice(1), {
|
|
134
|
-
stdio: 'inherit',
|
|
138
|
+
stdio: ['inherit', redactStdout ? 'pipe' : 'inherit', redactStderr ? 'pipe' : 'inherit'],
|
|
139
|
+
buffer: false,
|
|
135
140
|
env: { ...process.env, ...env }
|
|
136
141
|
})
|
|
137
142
|
|
|
143
|
+
if (redactStdout && child.stdout) {
|
|
144
|
+
const stdoutWriter = createRedactedStreamWriter(process.stdout, sensitiveValues, child.stdout)
|
|
145
|
+
child.stdout.on('data', stdoutWriter.write)
|
|
146
|
+
child.stdout.once('end', stdoutWriter.flush)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (redactStderr && child.stderr) {
|
|
150
|
+
const stderrWriter = createRedactedStreamWriter(process.stderr, sensitiveValues, child.stderr)
|
|
151
|
+
child.stderr.on('data', stderrWriter.write)
|
|
152
|
+
child.stderr.once('end', stderrWriter.flush)
|
|
153
|
+
}
|
|
154
|
+
|
|
138
155
|
process.on('SIGINT', sigintHandler)
|
|
139
156
|
process.on('SIGTERM', sigtermHandler)
|
|
140
157
|
|
|
@@ -157,7 +174,7 @@ async function executeCommand (commandArgs, env) {
|
|
|
157
174
|
if (error.code === 'ENOENT') {
|
|
158
175
|
logger.error(`Unknown command: ${error.command}`)
|
|
159
176
|
} else {
|
|
160
|
-
logger.error(error.message)
|
|
177
|
+
logger.error(redactOutput(error.message, sensitiveValues))
|
|
161
178
|
}
|
|
162
179
|
}
|
|
163
180
|
|
package/src/lib/helpers/fsx.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
|
+
const Errors = require('./errors')
|
|
2
3
|
|
|
3
4
|
const ENCODING = 'utf8'
|
|
4
5
|
|
|
@@ -19,11 +20,27 @@ function readFileXSync (filepath, encoding = null) {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
function writeFileXSync (filepath, str) {
|
|
22
|
-
|
|
23
|
+
try {
|
|
24
|
+
return fs.writeFileSync(filepath, str, ENCODING) // utf8 always
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (error.code === 'EACCES' || error.code === 'EPERM') {
|
|
27
|
+
throw new Errors({ filepath }).fileNotWritable()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
throw error
|
|
31
|
+
}
|
|
23
32
|
}
|
|
24
33
|
|
|
25
34
|
async function writeFileX (filepath, str) {
|
|
26
|
-
|
|
35
|
+
try {
|
|
36
|
+
return await fs.promises.writeFile(filepath, str, ENCODING)
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (error.code === 'EACCES' || error.code === 'EPERM') {
|
|
39
|
+
throw new Errors({ filepath }).fileNotWritable()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
throw error
|
|
43
|
+
}
|
|
27
44
|
}
|
|
28
45
|
|
|
29
46
|
async function exists (filepath) {
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const redact = require('./redact')
|
|
2
|
+
const { StringDecoder } = require('string_decoder')
|
|
3
|
+
|
|
4
|
+
function normalizedValues (values) {
|
|
5
|
+
return [...new Set((values || [])
|
|
6
|
+
.filter(value => value !== undefined && value !== null && `${value}`.length > 0)
|
|
7
|
+
.map(value => `${value}`))]
|
|
8
|
+
.sort((a, b) => b.length - a.length)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function redactOutput (value, sensitiveValues) {
|
|
12
|
+
const values = normalizedValues(sensitiveValues)
|
|
13
|
+
if (values.length < 1 || value === undefined || value === null) return value
|
|
14
|
+
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return value.map(item => redactOutput(item, values))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype) {
|
|
20
|
+
const result = {}
|
|
21
|
+
for (const [key, item] of Object.entries(value)) {
|
|
22
|
+
result[key] = redactOutput(item, values)
|
|
23
|
+
}
|
|
24
|
+
return result
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (typeof value !== 'string') return value
|
|
28
|
+
|
|
29
|
+
let result = value
|
|
30
|
+
for (const sensitiveValue of values) {
|
|
31
|
+
result = result.split(sensitiveValue).join(redact(sensitiveValue))
|
|
32
|
+
}
|
|
33
|
+
return result
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function partialMatchLength (value, sensitiveValues) {
|
|
37
|
+
let longest = 0
|
|
38
|
+
|
|
39
|
+
for (const sensitiveValue of sensitiveValues) {
|
|
40
|
+
const maxLength = Math.min(value.length, sensitiveValue.length - 1)
|
|
41
|
+
for (let length = maxLength; length > longest; length--) {
|
|
42
|
+
if (sensitiveValue.startsWith(value.slice(-length))) {
|
|
43
|
+
longest = length
|
|
44
|
+
break
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return longest
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function safeBoundary (value, boundary, sensitiveValues) {
|
|
53
|
+
let result = boundary
|
|
54
|
+
let changed = true
|
|
55
|
+
|
|
56
|
+
while (changed) {
|
|
57
|
+
changed = false
|
|
58
|
+
for (const sensitiveValue of sensitiveValues) {
|
|
59
|
+
let index = value.indexOf(sensitiveValue)
|
|
60
|
+
while (index !== -1) {
|
|
61
|
+
const end = index + sensitiveValue.length
|
|
62
|
+
if (index < result && end > result) {
|
|
63
|
+
result = index
|
|
64
|
+
changed = true
|
|
65
|
+
}
|
|
66
|
+
index = value.indexOf(sensitiveValue, index + 1)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return result
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createRedactedStreamWriter (stream, sensitiveValues, source) {
|
|
75
|
+
const values = normalizedValues(sensitiveValues)
|
|
76
|
+
const decoder = new StringDecoder('utf8')
|
|
77
|
+
let pending = ''
|
|
78
|
+
let waitingForDrain = false
|
|
79
|
+
|
|
80
|
+
const writeToStream = (value) => {
|
|
81
|
+
if (!value) return
|
|
82
|
+
|
|
83
|
+
const canContinue = stream.write(redactOutput(value, values))
|
|
84
|
+
if (!canContinue && source && !waitingForDrain) {
|
|
85
|
+
waitingForDrain = true
|
|
86
|
+
source.pause()
|
|
87
|
+
stream.once('drain', () => {
|
|
88
|
+
waitingForDrain = false
|
|
89
|
+
source.resume()
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const flush = () => {
|
|
95
|
+
pending += decoder.end()
|
|
96
|
+
if (!pending) return
|
|
97
|
+
writeToStream(pending)
|
|
98
|
+
pending = ''
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const write = (chunk) => {
|
|
102
|
+
pending += Buffer.isBuffer(chunk) ? decoder.write(chunk) : `${chunk}`
|
|
103
|
+
|
|
104
|
+
const holdbackLength = partialMatchLength(pending, values)
|
|
105
|
+
let boundary = pending.length - holdbackLength
|
|
106
|
+
boundary = safeBoundary(pending, boundary, values)
|
|
107
|
+
|
|
108
|
+
const output = pending.slice(0, boundary)
|
|
109
|
+
pending = pending.slice(boundary)
|
|
110
|
+
|
|
111
|
+
writeToStream(output)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { write, flush }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
redactOutput,
|
|
119
|
+
createRedactedStreamWriter
|
|
120
|
+
}
|
package/src/lib/main.d.ts
CHANGED
package/src/shared/colors.js
CHANGED
|
@@ -33,8 +33,8 @@ const colorsTrueColor = new Map([
|
|
|
33
33
|
['red', [140, 35, 50]] // #8C2332 brighter garnet
|
|
34
34
|
])
|
|
35
35
|
|
|
36
|
-
function getColor (color) {
|
|
37
|
-
const colorDepth = depth.getColorDepth()
|
|
36
|
+
function getColor (color, stream) {
|
|
37
|
+
const colorDepth = depth.getColorDepth(stream)
|
|
38
38
|
if (!colors256.has(color)) {
|
|
39
39
|
throw new Errors({ color }).invalidColor()
|
|
40
40
|
}
|
|
@@ -53,8 +53,8 @@ function getColor (color) {
|
|
|
53
53
|
return (message) => message
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
function bold (message) {
|
|
57
|
-
if (depth.getColorDepth() >= 4) {
|
|
56
|
+
function bold (message, stream) {
|
|
57
|
+
if (depth.getColorDepth(stream) >= 4) {
|
|
58
58
|
return `\x1b[1m${message}\x1b[22m`
|
|
59
59
|
}
|
|
60
60
|
|
package/src/shared/logger.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const packageJson = require('../lib/helpers/packageJson')
|
|
2
1
|
const Errors = require('../lib/helpers/errors')
|
|
3
2
|
const { getColor, bold } = require('./colors')
|
|
4
3
|
|
|
@@ -7,7 +6,6 @@ const levels = {
|
|
|
7
6
|
infoerror: 0,
|
|
8
7
|
warn: 1,
|
|
9
8
|
success: 2,
|
|
10
|
-
successv: 2,
|
|
11
9
|
info: 2,
|
|
12
10
|
help: 2,
|
|
13
11
|
verbose: 4,
|
|
@@ -15,23 +13,26 @@ const levels = {
|
|
|
15
13
|
silly: 6
|
|
16
14
|
}
|
|
17
15
|
|
|
18
|
-
const error = (m) => bold(getColor('red')(`☠ ${m}`))
|
|
19
|
-
const infoerror = getColor('gray') // actually an error
|
|
20
|
-
const warn = (m) => getColor('orangered')(`⚠ ${m}`)
|
|
21
|
-
const success = getColor('amber')
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const debug = (m) => getColor('plum')(`┆ ${m}`)
|
|
16
|
+
const error = (m, stream) => bold(getColor('red', stream)(`☠ ${m}`), stream)
|
|
17
|
+
const infoerror = (m, stream) => getColor('gray', stream)(m) // actually an error
|
|
18
|
+
const warn = (m, stream) => getColor('orangered', stream)(`⚠ ${m}`)
|
|
19
|
+
const success = (m, stream) => getColor('amber', stream)(m)
|
|
20
|
+
const info = (m, stream) => getColor('gray', stream)(m)
|
|
21
|
+
const help = (m, stream) => getColor('dodgerblue', stream)(m)
|
|
22
|
+
const verbose = (m, stream) => getColor('plum', stream)(`┆ ${m}`)
|
|
23
|
+
const debug = (m, stream) => getColor('plum', stream)(`┆ ${m}`)
|
|
27
24
|
|
|
28
25
|
let currentLevel = levels.info // default log level
|
|
29
|
-
let currentName = 'dotenvx' // default logger name
|
|
30
|
-
let currentVersion = packageJson.version // default logger version
|
|
31
26
|
|
|
32
27
|
function stderr (level, message) {
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
if (levels[level] === undefined) {
|
|
29
|
+
throw new Errors({ level }).missingLogLevel()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (levels[level] <= currentLevel) {
|
|
33
|
+
const formattedMessage = formatMessage(level, message, process.stderr)
|
|
34
|
+
console.error(formattedMessage)
|
|
35
|
+
}
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
function stdout (level, message) {
|
|
@@ -40,40 +41,38 @@ function stdout (level, message) {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
if (levels[level] <= currentLevel) {
|
|
43
|
-
const formattedMessage = formatMessage(level, message)
|
|
44
|
+
const formattedMessage = formatMessage(level, message, process.stdout)
|
|
44
45
|
console.log(formattedMessage)
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
function formatMessage (level, message) {
|
|
49
|
+
function formatMessage (level, message, stream) {
|
|
49
50
|
const formattedMessage = typeof message === 'object' ? JSON.stringify(message) : message
|
|
50
51
|
|
|
51
52
|
switch (level.toLowerCase()) {
|
|
52
53
|
// errors
|
|
53
54
|
case 'error':
|
|
54
|
-
return error(formattedMessage)
|
|
55
|
+
return error(formattedMessage, stream)
|
|
55
56
|
case 'infoerror':
|
|
56
|
-
return infoerror(formattedMessage)
|
|
57
|
+
return infoerror(formattedMessage, stream)
|
|
57
58
|
// warns
|
|
58
59
|
case 'warn':
|
|
59
|
-
return warn(formattedMessage)
|
|
60
|
+
return warn(formattedMessage, stream)
|
|
60
61
|
// successes
|
|
61
62
|
case 'success':
|
|
62
|
-
return success(formattedMessage)
|
|
63
|
-
case 'successv': // success with 'version'
|
|
64
|
-
return successv(`${formattedMessage} · ${currentName}@${currentVersion}`)
|
|
63
|
+
return success(formattedMessage, stream)
|
|
65
64
|
// info
|
|
66
65
|
case 'info':
|
|
67
|
-
return info(formattedMessage)
|
|
66
|
+
return info(formattedMessage, stream)
|
|
68
67
|
// help
|
|
69
68
|
case 'help':
|
|
70
|
-
return help(formattedMessage)
|
|
69
|
+
return help(formattedMessage, stream)
|
|
71
70
|
// verbose
|
|
72
71
|
case 'verbose':
|
|
73
|
-
return verbose(formattedMessage)
|
|
72
|
+
return verbose(formattedMessage, stream)
|
|
74
73
|
// debug
|
|
75
74
|
case 'debug':
|
|
76
|
-
return debug(formattedMessage)
|
|
75
|
+
return debug(formattedMessage, stream)
|
|
77
76
|
}
|
|
78
77
|
}
|
|
79
78
|
|
|
@@ -85,18 +84,17 @@ const logger = {
|
|
|
85
84
|
error: (msg) => stderr('error', msg),
|
|
86
85
|
infoerror: (msg) => stderr('infoerror', msg),
|
|
87
86
|
// warns
|
|
88
|
-
warn: (msg) =>
|
|
87
|
+
warn: (msg) => stderr('warn', msg),
|
|
89
88
|
// success
|
|
90
|
-
success: (msg) =>
|
|
91
|
-
successv: (msg) => stdout('successv', msg),
|
|
89
|
+
success: (msg) => stderr('success', msg),
|
|
92
90
|
// info
|
|
93
91
|
info: (msg) => stdout('info', msg),
|
|
94
92
|
// help
|
|
95
|
-
help: (msg) =>
|
|
93
|
+
help: (msg) => stderr('help', msg),
|
|
96
94
|
// verbose
|
|
97
|
-
verbose: (msg) =>
|
|
95
|
+
verbose: (msg) => stderr('verbose', msg),
|
|
98
96
|
// debug
|
|
99
|
-
debug: (msg) =>
|
|
97
|
+
debug: (msg) => stderr('debug', msg),
|
|
100
98
|
setLevel: (level) => {
|
|
101
99
|
if (levels[level] !== undefined) {
|
|
102
100
|
currentLevel = levels[level]
|
|
@@ -104,11 +102,9 @@ const logger = {
|
|
|
104
102
|
}
|
|
105
103
|
},
|
|
106
104
|
setName: (name) => {
|
|
107
|
-
currentName = name
|
|
108
105
|
logger.name = name
|
|
109
106
|
},
|
|
110
107
|
setVersion: (version) => {
|
|
111
|
-
currentVersion = version
|
|
112
108
|
logger.version = version
|
|
113
109
|
}
|
|
114
110
|
}
|