@dotenvx/dotenvx 1.57.0 → 1.57.1

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 CHANGED
@@ -2,7 +2,22 @@
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/v1.57.0...main)
5
+ [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.57.1...main)
6
+
7
+ ## [1.57.1](https://github.com/dotenvx/dotenvx/compare/v1.57.0...v1.57.1) (2026-03-21)
8
+
9
+ ### Changes
10
+
11
+ * improved error logs and compacted most to a single line ([#755](https://github.com/dotenvx/dotenvx/pull/755))
12
+ * introduced leading log glyphs as a visual status language:
13
+
14
+ * `◈` success action (encrypted)
15
+ * `◇` success action (set plain value, decrypted)
16
+ * `⟳` success action (rotated)
17
+ * `○` informational no-op (no changes)
18
+ * `▣` success action for generated/updated support files
19
+ * `⚠` warning
20
+ * `☠` error
6
21
 
7
22
  ## [1.57.0](https://github.com/dotenvx/dotenvx/compare/v1.56.0...v1.57.0) (2026-03-19)
8
23
 
@@ -1783,4 +1798,3 @@ prevent committing a `.env*` file to code. append to `.gitignore`, `.dockerignor
1783
1798
  ## 0.3.9 and prior
1784
1799
 
1785
1800
  Please see commit history.
1786
-
package/README.md CHANGED
@@ -30,7 +30,20 @@ console.log(`Hello ${process.env.HELLO}`)
30
30
 
31
31
  or install globally - *unlocks dotenv for any language, framework, or platform!*
32
32
 
33
- <details><summary>with curl 🌐 </summary><br>
33
+ <details><summary>with npm 🌍</summary><br>
34
+
35
+ ```sh
36
+ npm i -g @dotenvx/dotenvx
37
+ dotenvx help
38
+ ```
39
+
40
+ [![npm installs](https://img.shields.io/npm/dt/@dotenvx/dotenvx)](https://npmjs.com/@dotenvx/dotenvx)
41
+
42
+ &nbsp;
43
+
44
+ </details>
45
+
46
+ <details><summary>with curl 🌐</summary><br>
34
47
 
35
48
  ```sh
36
49
  curl -sfS https://dotenvx.sh | sh
@@ -936,8 +949,7 @@ For example, when missing a custom .env file:
936
949
 
937
950
  ```sh
938
951
  $ dotenvx run -f .env.missing -- echo $HELLO
939
- [MISSING_ENV_FILE] missing .env.missing file (/Users/scottmotte/Code/dotenvx/playground/apr-16/.env.missing)
940
- [MISSING_ENV_FILE] https://github.com/dotenvx/dotenvx/issues/484 and re-run [dotenvx run -- echo]
952
+ [MISSING_ENV_FILE] missing file (/Users/scottmotte/Code/dotenvx/playground/apr-16/.env.missing). fix: [echo "HELLO=World" > .env.missing]
941
953
  ```
942
954
 
943
955
  or when missing a KEY:
@@ -945,7 +957,7 @@ or when missing a KEY:
945
957
  ```sh
946
958
  $ echo "HELLO=World" > .env
947
959
  $ dotenvx get GOODBYE
948
- [MISSING_KEY] missing GOODBYE key
960
+ [MISSING_KEY] missing key (GOODBYE)
949
961
  ```
950
962
 
951
963
  </details>
@@ -1172,8 +1184,7 @@ Exit with code `1` if any errors are encountered - like a missing .env file or d
1172
1184
  $ echo "console.log('Hello ' + process.env.HELLO)" > index.js
1173
1185
 
1174
1186
  $ dotenvx run -f .env.missing --strict -- node index.js
1175
- [MISSING_ENV_FILE] missing .env.missing file (/path/to/.env.missing)
1176
- [MISSING_ENV_FILE] ? add one with [echo "HELLO=World" > .env.missing]
1187
+ [MISSING_ENV_FILE] missing file (/path/to/.env.missing). fix: [echo "HELLO=World" > .env.missing]
1177
1188
  ```
1178
1189
 
1179
1190
  This can be useful in `ci` scripts where you want to fail the ci if your `.env` file could not be decrypted at runtime.
@@ -1326,7 +1337,7 @@ Exit with code `1` if any errors are encountered - like a missing key, missing .
1326
1337
 
1327
1338
  ```sh
1328
1339
  $ dotenvx get DOES_NOT_EXIST --strict
1329
- [MISSING_KEY] missing DOES_NOT_EXIST key
1340
+ [MISSING_KEY] missing key (DOES_NOT_EXIST)
1330
1341
  ```
1331
1342
 
1332
1343
  </details>
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.57.0",
2
+ "version": "1.57.1",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a secure dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -2,7 +2,6 @@ const fsx = require('./../../lib/helpers/fsx')
2
2
  const { logger } = require('./../../shared/logger')
3
3
 
4
4
  const Decrypt = require('./../../lib/services/decrypt')
5
-
6
5
  const catchAndLog = require('../../lib/helpers/catchAndLog')
7
6
 
8
7
  function decrypt () {
@@ -23,10 +22,7 @@ function decrypt () {
23
22
  for (const processedEnv of processedEnvs) {
24
23
  if (processedEnv.error) {
25
24
  errorCount += 1
26
- logger.error(processedEnv.error.message)
27
- if (processedEnv.error.help) {
28
- logger.error(processedEnv.error.help)
29
- }
25
+ logger.error(processedEnv.error.messageWithHelp)
30
26
  } else {
31
27
  console.log(processedEnv.envSrc)
32
28
  }
@@ -50,16 +46,7 @@ function decrypt () {
50
46
 
51
47
  if (processedEnv.error) {
52
48
  errorCount += 1
53
-
54
- if (processedEnv.error.code === 'MISSING_ENV_FILE') {
55
- logger.error(processedEnv.error.message)
56
- logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx decrypt]`)
57
- } else {
58
- logger.error(processedEnv.error.message)
59
- if (processedEnv.error.help) {
60
- logger.error(processedEnv.error.help)
61
- }
62
- }
49
+ logger.error(processedEnv.error.messageWithHelp)
63
50
  } else if (processedEnv.changed) {
64
51
  fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
65
52
 
@@ -34,15 +34,7 @@ function encrypt () {
34
34
  for (const processedEnv of processedEnvs) {
35
35
  logger.verbose(`encrypting ${processedEnv.envFilepath} (${processedEnv.filepath})`)
36
36
  if (processedEnv.error) {
37
- if (processedEnv.error.code === 'MISSING_ENV_FILE') {
38
- logger.warn(processedEnv.error.message)
39
- logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx encrypt]`)
40
- } else {
41
- logger.warn(processedEnv.error.message)
42
- if (processedEnv.error.help) {
43
- logger.help(processedEnv.error.help)
44
- }
45
- }
37
+ logger.warn(processedEnv.error.messageWithHelp)
46
38
  } else if (processedEnv.changed) {
47
39
  fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
48
40
 
@@ -2,6 +2,7 @@ const fsx = require('./../../../lib/helpers/fsx')
2
2
  const path = require('path')
3
3
  const main = require('./../../../lib/main')
4
4
  const { logger } = require('./../../../shared/logger')
5
+ const catchAndLog = require('./../../../lib/helpers/catchAndLog')
5
6
 
6
7
  function genexample (directory) {
7
8
  logger.debug(`directory: ${directory}`)
@@ -27,13 +28,7 @@ function genexample (directory) {
27
28
  logger.info('○ no changes (.env.example)')
28
29
  }
29
30
  } catch (error) {
30
- logger.error(error.message)
31
- if (error.help) {
32
- logger.help(error.help)
33
- }
34
- if (error.code) {
35
- logger.debug(`ERROR_CODE: ${error.code}`)
36
- }
31
+ catchAndLog(error)
37
32
  process.exit(1)
38
33
  }
39
34
  }
@@ -1,6 +1,7 @@
1
1
  const { logger } = require('./../../../shared/logger')
2
2
 
3
3
  const Prebuild = require('./../../../lib/services/prebuild')
4
+ const catchAndLog = require('./../../../lib/helpers/catchAndLog')
4
5
 
5
6
  function prebuild (directory) {
6
7
  // debug args
@@ -16,19 +17,12 @@ function prebuild (directory) {
16
17
  } = new Prebuild(directory, options).run()
17
18
 
18
19
  for (const warning of warnings) {
19
- logger.warn(warning.message)
20
- if (warning.help) {
21
- logger.help(warning.help)
22
- }
20
+ logger.warn(warning.messageWithHelp)
23
21
  }
24
22
 
25
23
  logger.success(successMessage)
26
24
  } catch (error) {
27
- logger.error(error.message)
28
- if (error.help) {
29
- logger.help(error.help)
30
- }
31
-
25
+ catchAndLog(error)
32
26
  process.exit(1)
33
27
  }
34
28
  }
@@ -1,6 +1,7 @@
1
1
  const { logger } = require('./../../../shared/logger')
2
2
 
3
3
  const Precommit = require('./../../../lib/services/precommit')
4
+ const catchAndLog = require('./../../../lib/helpers/catchAndLog')
4
5
 
5
6
  function precommit (directory) {
6
7
  // debug args
@@ -16,19 +17,12 @@ function precommit (directory) {
16
17
  } = new Precommit(directory, options).run()
17
18
 
18
19
  for (const warning of warnings) {
19
- logger.warn(warning.message)
20
- if (warning.help) {
21
- logger.help(warning.help)
22
- }
20
+ logger.warn(warning.messageWithHelp)
23
21
  }
24
22
 
25
23
  logger.success(successMessage)
26
24
  } catch (error) {
27
- logger.error(error.message)
28
- if (error.help) {
29
- logger.help(error.help)
30
- }
31
-
25
+ catchAndLog(error)
32
26
  process.exit(1)
33
27
  }
34
28
  }
@@ -12,8 +12,8 @@ function scan () {
12
12
  childProcess.execSync('gitleaks version', { stdio: ['ignore', 'pipe', 'ignore'] })
13
13
  } catch (error) {
14
14
  logger.error('gitleaks: command not found')
15
- logger.help('? install gitleaks: [brew install gitleaks]')
16
- logger.help('? other install options: [https://github.com/gitleaks/gitleaks]')
15
+ logger.help('fix: install gitleaks: [brew install gitleaks]')
16
+ logger.help('fix: other install options: [https://github.com/gitleaks/gitleaks]')
17
17
  process.exit(1)
18
18
  return
19
19
  }
@@ -2,6 +2,7 @@ const { logger } = require('./../../shared/logger')
2
2
 
3
3
  const conventions = require('./../../lib/helpers/conventions')
4
4
  const escape = require('./../../lib/helpers/escape')
5
+ const catchAndLog = require('./../../lib/helpers/catchAndLog')
5
6
 
6
7
  const Get = require('./../../lib/services/get')
7
8
 
@@ -35,10 +36,7 @@ function get (key) {
35
36
  continue // ignore error
36
37
  }
37
38
 
38
- logger.error(error.message)
39
- if (error.help) {
40
- logger.error(error.help)
41
- }
39
+ logger.error(error.messageWithHelp)
42
40
  }
43
41
 
44
42
  if (key) {
@@ -75,10 +73,7 @@ function get (key) {
75
73
  }
76
74
  }
77
75
  } catch (error) {
78
- logger.error(error.message)
79
- if (error.help) {
80
- logger.error(error.help)
81
- }
76
+ catchAndLog(error)
82
77
  process.exit(1)
83
78
  }
84
79
  }
@@ -38,15 +38,7 @@ function rotate () {
38
38
  for (const processedEnv of processedEnvs) {
39
39
  logger.verbose(`rotating ${processedEnv.envFilepath} (${processedEnv.filepath})`)
40
40
  if (processedEnv.error) {
41
- if (processedEnv.error.code === 'MISSING_ENV_FILE') {
42
- logger.warn(processedEnv.error.message)
43
- logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx rotate]`)
44
- } else {
45
- logger.warn(processedEnv.error.message)
46
- if (processedEnv.error.help) {
47
- logger.help(processedEnv.error.help)
48
- }
49
- }
41
+ logger.warn(processedEnv.error.messageWithHelp)
50
42
  } else if (processedEnv.changed) {
51
43
  fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
52
44
  if (processedEnv.privateKeyAdded) {
@@ -3,6 +3,7 @@ const { logger } = require('./../../shared/logger')
3
3
 
4
4
  const executeCommand = require('./../../lib/helpers/executeCommand')
5
5
  const Run = require('./../../lib/services/run')
6
+ const catchAndLog = require('./../../lib/helpers/catchAndLog')
6
7
 
7
8
  const conventions = require('./../../lib/helpers/conventions')
8
9
 
@@ -64,18 +65,10 @@ async function run () {
64
65
 
65
66
  if (options.strict) throw error // throw if strict and not ignored
66
67
 
67
- if (error.code === 'MISSING_ENV_FILE') {
68
- if (!options.convention) { // do not output error for conventions (too noisy)
69
- logger.error(error.message)
70
- if (error.help) {
71
- logger.error(`${error.help} and re-run [dotenvx run -- ${commandArgs.join(' ')}]`)
72
- }
73
- }
68
+ if (error.code === 'MISSING_ENV_FILE' && options.convention) { // do not output error for conventions (too noisy)
69
+ // intentionally quiet
74
70
  } else {
75
- logger.error(error.message)
76
- if (error.help) {
77
- logger.error(error.help)
78
- }
71
+ logger.error(error.messageWithHelp)
79
72
  }
80
73
  }
81
74
 
@@ -106,10 +99,7 @@ async function run () {
106
99
 
107
100
  logger.successv(msg)
108
101
  } catch (error) {
109
- logger.error(error.message)
110
- if (error.help) {
111
- logger.error(error.help)
112
- }
102
+ catchAndLog(error)
113
103
  process.exit(1)
114
104
  }
115
105
 
@@ -40,15 +40,7 @@ function set (key, value) {
40
40
  logger.verbose(`setting for ${processedEnv.envFilepath}`)
41
41
 
42
42
  if (processedEnv.error) {
43
- if (processedEnv.error.code === 'MISSING_ENV_FILE') {
44
- logger.warn(processedEnv.error.message)
45
- logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx set]`)
46
- } else {
47
- logger.warn(processedEnv.error.message)
48
- if (processedEnv.error.help) {
49
- logger.help(processedEnv.error.help)
50
- }
51
- }
43
+ logger.warn(processedEnv.error.messageWithHelp)
52
44
  } else {
53
45
  fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
54
46
 
@@ -30,8 +30,7 @@ const commanderVersion = getCommanderVersion()
30
30
  if (commanderVersion && parseInt(commanderVersion.split('.')[0], 10) >= 12) {
31
31
  const message = `dotenvx depends on commander@11.x.x but you are attempting to hoist commander@${commanderVersion}`
32
32
  const error = new Errors({ message }).dangerousDependencyHoist()
33
- logger.error(error.message)
34
- if (error.help) logger.error(error.help)
33
+ logger.error(error.messageWithHelp)
35
34
  }
36
35
 
37
36
  // global log levels
@@ -1,10 +1,7 @@
1
1
  const { logger } = require('./../../shared/logger')
2
2
 
3
3
  function catchAndLog (error) {
4
- logger.error(error.message)
5
- if (error.help) {
6
- logger.help(error.help)
7
- }
4
+ logger.error(error.messageWithHelp)
8
5
  if (error.debug) {
9
6
  logger.debug(error.debug)
10
7
  }
@@ -1,3 +1,5 @@
1
+ const Errors = require('./errors')
2
+
1
3
  function conventions (convention) {
2
4
  const env = process.env.DOTENV_ENV || process.env.NODE_ENV || 'development'
3
5
 
@@ -19,7 +21,7 @@ function conventions (convention) {
19
21
  { type: 'envFile', value: '.env.defaults' }
20
22
  ]
21
23
  } else {
22
- throw new Error(`INVALID_CONVENTION: '${convention}'. permitted conventions: ['nextjs', 'flow']`)
24
+ throw new Errors({ convention }).invalidConvention()
23
25
  }
24
26
  }
25
27
 
@@ -30,7 +30,7 @@ function decryptKeyValue (key, value, privateKeyName, privateKey) {
30
30
  if (e.message === 'Invalid private key') {
31
31
  decryptionError = new Errors({ key, privateKeyName, privateKey }).invalidPrivateKey()
32
32
  } else if (e.message === 'Unsupported state or unable to authenticate data') {
33
- decryptionError = new Errors({ key, privateKeyName, privateKey }).looksWrongPrivateKey()
33
+ decryptionError = new Errors({ key, privateKeyName, privateKey }).wrongPrivateKey()
34
34
  } else if (e.message === 'Point of length 65 was invalid. Expected 33 compressed bytes or 65 uncompressed bytes') {
35
35
  decryptionError = new Errors({ key, privateKeyName, privateKey }).malformedEncryptedData()
36
36
  } else {
@@ -1,5 +1,26 @@
1
1
  const truncate = require('./truncate')
2
2
 
3
+ const ISSUE_BY_CODE = {
4
+ COMMAND_EXITED_WITH_CODE: 'https://github.com/dotenvx/dotenvx/issues/new',
5
+ COMMAND_SUBSTITUTION_FAILED: 'https://github.com/dotenvx/dotenvx/issues/532',
6
+ DECRYPTION_FAILED: 'https://github.com/dotenvx/dotenvx/issues/757',
7
+ DANGEROUS_DEPENDENCY_HOIST: 'https://github.com/dotenvx/dotenvx/issues/622',
8
+ INVALID_COLOR: 'must be 256 colors',
9
+ INVALID_CONVENTION: 'https://github.com/dotenvx/dotenvx/issues/761',
10
+ INVALID_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/465',
11
+ INVALID_PUBLIC_KEY: 'https://github.com/dotenvx/dotenvx/issues/756',
12
+ MALFORMED_ENCRYPTED_DATA: 'https://github.com/dotenvx/dotenvx/issues/467',
13
+ MISPAIRED_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/752',
14
+ MISSING_DIRECTORY: 'https://github.com/dotenvx/dotenvx/issues/758',
15
+ MISSING_ENV_FILE: 'https://github.com/dotenvx/dotenvx/issues/484',
16
+ MISSING_ENV_FILES: 'https://github.com/dotenvx/dotenvx/issues/760',
17
+ MISSING_KEY: 'https://github.com/dotenvx/dotenvx/issues/759',
18
+ MISSING_LOG_LEVEL: 'must be valid log level',
19
+ MISSING_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/464',
20
+ PRECOMMIT_HOOK_MODIFY_FAILED: 'try again or report error',
21
+ WRONG_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/466'
22
+ }
23
+
3
24
  class Errors {
4
25
  constructor (options = {}) {
5
26
  this.filepath = options.filepath
@@ -8,118 +29,252 @@ class Errors {
8
29
  this.key = options.key
9
30
  this.privateKey = options.privateKey
10
31
  this.privateKeyName = options.privateKeyName
32
+ this.publicKeyName = options.publicKeyName
11
33
  this.publicKey = options.publicKey
12
34
  this.publicKeyExisting = options.publicKeyExisting
13
35
  this.command = options.command
14
36
 
15
37
  this.message = options.message
38
+ this.code = options.code
39
+ this.help = options.help
40
+ this.debug = options.debug
41
+
42
+ this.convention = options.convention
43
+ this.directory = options.directory
44
+ this.exitCode = options.exitCode
45
+ this.level = options.level
46
+ this.color = options.color
47
+ this.error = options.error
16
48
  }
17
49
 
18
- missingEnvFile () {
19
- const code = 'MISSING_ENV_FILE'
20
- const message = `[${code}] missing ${this.envFilepath} file (${this.filepath})`
21
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/484`
50
+ custom () {
51
+ const e = new Error(this.message)
52
+ if (this.code) e.code = this.code
53
+ if (this.help) e.help = this.help
54
+ if (this.code && !e.help) e.help = `fix: [${ISSUE_BY_CODE[this.code]}]`
55
+ e.messageWithHelp = `${e.message}. ${e.help}`
56
+ if (this.debug) e.debug = this.debug
57
+ return e
58
+ }
59
+
60
+ commandExitedWithCode () {
61
+ const code = 'COMMAND_EXITED_WITH_CODE'
62
+ const message = `[${code}] Command exited with exit code ${this.exitCode}`
63
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
22
64
 
23
65
  const e = new Error(message)
24
66
  e.code = code
25
67
  e.help = help
68
+ e.messageWithHelp = `${message}. ${help}`
26
69
  return e
27
70
  }
28
71
 
29
- missingKey () {
30
- const code = 'MISSING_KEY'
31
- const message = `[${code}] missing ${this.key} key`
72
+ commandSubstitutionFailed () {
73
+ const code = 'COMMAND_SUBSTITUTION_FAILED'
74
+ const message = `[${code}] could not eval ${this.key} containing command '${this.command}': ${this.message}`
75
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
32
76
 
33
77
  const e = new Error(message)
34
78
  e.code = code
79
+ e.help = help
80
+ e.messageWithHelp = `${message}. ${help}`
35
81
  return e
36
82
  }
37
83
 
38
- missingPrivateKey () {
39
- const code = 'MISSING_PRIVATE_KEY'
40
- const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
41
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/464`
84
+ decryptionFailed () {
85
+ const code = 'DECRYPTION_FAILED'
86
+ const message = `[${code}] ${this.message}`
87
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
42
88
 
43
89
  const e = new Error(message)
44
90
  e.code = code
45
91
  e.help = help
92
+ e.messageWithHelp = `${message}. ${help}`
93
+ return e
94
+ }
95
+
96
+ dangerousDependencyHoist () {
97
+ const code = 'DANGEROUS_DEPENDENCY_HOIST'
98
+ const message = `[${code}] your environment has hoisted an incompatible version of a dotenvx dependency: ${this.message}`
99
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
100
+
101
+ const e = new Error(message)
102
+ e.code = code
103
+ e.help = help
104
+ e.messageWithHelp = `${message}. ${help}`
105
+ return e
106
+ }
107
+
108
+ invalidColor () {
109
+ const code = 'INVALID_COLOR'
110
+ const message = `[${code}] Invalid color ${this.color}`
111
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
112
+
113
+ const e = new Error(message)
114
+ e.code = code
115
+ e.help = help
116
+ e.messageWithHelp = `${message}. ${help}`
117
+ return e
118
+ }
119
+
120
+ invalidConvention () {
121
+ const code = 'INVALID_CONVENTION'
122
+ const message = `[${code}] invalid convention (${this.convention})`
123
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
124
+
125
+ const e = new Error(message)
126
+ e.code = code
127
+ e.help = help
128
+ e.messageWithHelp = `${message}. ${help}`
46
129
  return e
47
130
  }
48
131
 
49
132
  invalidPrivateKey () {
50
133
  const code = 'INVALID_PRIVATE_KEY'
51
134
  const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
52
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/465`
135
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
53
136
 
54
137
  const e = new Error(message)
55
138
  e.code = code
56
139
  e.help = help
140
+ e.messageWithHelp = `${message}. ${help}`
141
+ return e
142
+ }
143
+
144
+ invalidPublicKey () {
145
+ const code = 'INVALID_PUBLIC_KEY'
146
+ const message = `[${code}] could not encrypt using public key '${this.publicKeyName}=${truncate(this.publicKey)}'`
147
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
148
+
149
+ const e = new Error(message)
150
+ e.code = code
151
+ e.help = help
152
+ e.messageWithHelp = `${message}. ${help}`
153
+ return e
154
+ }
155
+
156
+ malformedEncryptedData () {
157
+ const code = 'MALFORMED_ENCRYPTED_DATA'
158
+ const message = `[${code}] could not decrypt ${this.key} because encrypted data appears malformed`
159
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
160
+
161
+ const e = new Error(message)
162
+ e.code = code
163
+ e.help = help
164
+ e.messageWithHelp = `${message}. ${help}`
57
165
  return e
58
166
  }
59
167
 
60
168
  mispairedPrivateKey () {
61
169
  const code = 'MISPAIRED_PRIVATE_KEY'
62
170
  const message = `[${code}] private key's derived public key (${truncate(this.publicKey)}) does not match the existing public key (${truncate(this.publicKeyExisting)})`
63
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/752`
171
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
64
172
 
65
173
  const e = new Error(message)
66
174
  e.code = code
67
175
  e.help = help
176
+ e.messageWithHelp = `${message}. ${help}`
68
177
  return e
69
178
  }
70
179
 
71
- looksWrongPrivateKey () {
72
- const code = 'WRONG_PRIVATE_KEY'
73
- const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
74
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/466`
180
+ missingDirectory () {
181
+ const code = 'MISSING_DIRECTORY'
182
+ const message = `[${code}] missing directory (${this.directory})`
183
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
75
184
 
76
185
  const e = new Error(message)
77
186
  e.code = code
78
187
  e.help = help
188
+ e.messageWithHelp = `${message}. ${help}`
79
189
  return e
80
190
  }
81
191
 
82
- malformedEncryptedData () {
83
- const code = 'MALFORMED_ENCRYPTED_DATA'
84
- const message = `[${code}] could not decrypt ${this.key} because encrypted data appears malformed`
85
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/467`
192
+ missingEnvFile () {
193
+ const code = 'MISSING_ENV_FILE'
194
+ const envFilepath = this.envFilepath || '.env'
195
+ const message = `[${code}] missing file (${envFilepath})`
196
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
86
197
 
87
198
  const e = new Error(message)
88
199
  e.code = code
89
200
  e.help = help
201
+ e.messageWithHelp = `${message}. ${help}`
90
202
  return e
91
203
  }
92
204
 
93
- decryptionFailed () {
94
- const code = 'DECRYPTION_FAILED'
95
- const message = this.message
205
+ missingEnvFiles () {
206
+ const code = 'MISSING_ENV_FILES'
207
+ const message = `[${code}] no .env* files found`
208
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
96
209
 
97
210
  const e = new Error(message)
98
211
  e.code = code
212
+ e.help = help
213
+ e.messageWithHelp = `${message}. ${help}`
99
214
  return e
100
215
  }
101
216
 
102
- commandSubstitutionFailed () {
103
- const code = 'COMMAND_SUBSTITUTION_FAILED'
104
- const message = `[${code}] could not eval ${this.key} containing command '${this.command}': ${this.message}`
105
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/532`
217
+ missingKey () {
218
+ const code = 'MISSING_KEY'
219
+ const message = `[${code}] missing key (${this.key})`
220
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
106
221
 
107
222
  const e = new Error(message)
108
223
  e.code = code
109
224
  e.help = help
225
+ e.messageWithHelp = `${message}. ${help}`
110
226
  return e
111
227
  }
112
228
 
113
- dangerousDependencyHoist () {
114
- const code = 'DANGEROUS_DEPENDENCY_HOIST'
115
- const message = `[${code}] your environment has hoisted an incompatible version of a dotenvx dependency: ${this.message}`
116
- const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/622`
229
+ missingLogLevel () {
230
+ const code = 'MISSING_LOG_LEVEL'
231
+ const message = `[${code}] missing log level '${this.level}'. implement in logger`
232
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
233
+
234
+ const e = new Error(message)
235
+ e.code = code
236
+ e.help = help
237
+ e.messageWithHelp = `${message}. ${help}`
238
+ return e
239
+ }
240
+
241
+ missingPrivateKey () {
242
+ const code = 'MISSING_PRIVATE_KEY'
243
+ const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
244
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
245
+
246
+ const e = new Error(message)
247
+ e.code = code
248
+ e.help = help
249
+ e.messageWithHelp = `${message}. ${help}`
250
+ return e
251
+ }
252
+
253
+ precommitHookModifyFailed () {
254
+ const code = 'PRECOMMIT_HOOK_MODIFY_FAILED'
255
+ const message = `[${code}] failed to modify pre-commit hook: ${this.error.message}`
256
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
117
257
 
118
258
  const e = new Error(message)
119
259
  e.code = code
120
260
  e.help = help
261
+ e.messageWithHelp = `${message}. ${help}`
262
+ return e
263
+ }
264
+
265
+ wrongPrivateKey () {
266
+ const code = 'WRONG_PRIVATE_KEY'
267
+ const message = `[${code}] could not decrypt ${this.key} using private key '${this.privateKeyName}=${truncate(this.privateKey)}'`
268
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
269
+
270
+ const e = new Error(message)
271
+ e.code = code
272
+ e.help = help
273
+ e.messageWithHelp = `${message}. ${help}`
121
274
  return e
122
275
  }
123
276
  }
124
277
 
278
+ Errors.ISSUE_BY_CODE = ISSUE_BY_CODE
279
+
125
280
  module.exports = Errors
@@ -2,6 +2,7 @@ const path = require('path')
2
2
  const which = require('which')
3
3
  const execute = require('./../../lib/helpers/execute')
4
4
  const { logger } = require('./../../shared/logger')
5
+ const Errors = require('./errors')
5
6
 
6
7
  async function executeCommand (commandArgs, env) {
7
8
  const signals = [
@@ -91,7 +92,7 @@ async function executeCommand (commandArgs, env) {
91
92
 
92
93
  if (exitCode !== 0) {
93
94
  logger.debug(`received exitCode ${exitCode}`)
94
- throw new Error(`Command exited with exit code ${exitCode}`)
95
+ throw new Errors({ exitCode }).commandExitedWithCode()
95
96
  }
96
97
  } catch (error) {
97
98
  // no color on these errors as they can be standard errors for things like jest exiting with exitCode 1 for a single failed test.
@@ -1,7 +1,50 @@
1
1
  const path = require('path')
2
+ const fs = require('fs')
2
3
  const childProcess = require('child_process')
3
4
  const { logger } = require('../../shared/logger')
4
5
 
6
+ function installCommandForOps () {
7
+ const userAgent = process.env.npm_config_user_agent || ''
8
+ if (userAgent.startsWith('pnpm/')) return 'pnpm add -g @dotenvx/dotenvx-ops'
9
+ if (userAgent.startsWith('yarn/')) return 'yarn global add @dotenvx/dotenvx-ops'
10
+ if (userAgent.startsWith('npm/')) return 'npm i -g @dotenvx/dotenvx-ops'
11
+
12
+ const cwd = process.cwd()
13
+ if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) return 'pnpm add -g @dotenvx/dotenvx-ops'
14
+ if (fs.existsSync(path.join(cwd, 'yarn.lock'))) return 'yarn global add @dotenvx/dotenvx-ops'
15
+ if (
16
+ fs.existsSync(path.join(cwd, 'package-lock.json')) ||
17
+ fs.existsSync(path.join(cwd, 'npm-shrinkwrap.json'))
18
+ ) return 'npm i -g @dotenvx/dotenvx-ops'
19
+
20
+ if (fs.existsSync(path.join(cwd, 'package.json'))) return 'npm i -g @dotenvx/dotenvx-ops'
21
+
22
+ return 'curl -sfS https://dotenvx.sh/ops | sh'
23
+ }
24
+
25
+ function opsBanner (installCommand) {
26
+ const lines = [
27
+ '',
28
+ ' ██████╗ ██████╗ ███████╗',
29
+ ' ██╔═══██╗██╔══██╗██╔════╝',
30
+ ' ██║ ██║██████╔╝███████╗',
31
+ ' ██║ ██║██╔═══╝ ╚════██║',
32
+ ' ╚██████╔╝██║ ███████║',
33
+ ' ╚═════╝ ╚═╝ ╚══════╝',
34
+ '',
35
+ ' KEYS OFF COMPUTER: Add hardened key protection with dotenvx-ops.',
36
+ ` Install now: [${installCommand}]`,
37
+ ' Learn more: [https://dotenvx.com/ops]'
38
+ ]
39
+
40
+ const innerWidth = Math.max(67, ...lines.map((line) => line.length))
41
+ const top = ` ${'_'.repeat(innerWidth)}`
42
+ const middle = lines.map((line) => `|${line.padEnd(innerWidth)}|`).join('\n')
43
+ const bottom = `|${'_'.repeat(innerWidth)}|`
44
+
45
+ return `${top}\n${middle}\n${bottom}`
46
+ }
47
+
5
48
  function executeDynamic (program, command, rawArgs) {
6
49
  if (!command) {
7
50
  program.outputHelp()
@@ -23,23 +66,8 @@ function executeDynamic (program, command, rawArgs) {
23
66
  const result = childProcess.spawnSync(`dotenvx-${command}`, forwardedArgs, { stdio: 'inherit', env })
24
67
  if (result.error) {
25
68
  if (command === 'ops') {
26
- const ops = ` ___________________________________________________________________
27
- | |
28
- | ██████╗ ██████╗ ███████╗ |
29
- | ██╔═══██╗██╔══██╗██╔════╝ |
30
- | ██║ ██║██████╔╝███████╗ |
31
- | ██║ ██║██╔═══╝ ╚════██║ |
32
- | ╚██████╔╝██║ ███████║ |
33
- | ╚═════╝ ╚═╝ ╚══════╝ |
34
- | |
35
- | Learn more at [https://dotenvx.com/ops] |
36
- |___________________________________________________________________|`
37
-
38
- console.log(ops)
39
- console.log('')
40
- logger.warn(`[INSTALLATION_NEEDED] install dotenvx-${command} to use [dotenvx ${command}] 🛡️`)
41
- logger.help('⮕ next run: [curl -sfS https://dotenvx.sh/ops | sh]')
42
- logger.help('⮕ see more: [https://dotenvx.com/ops]')
69
+ const installCommand = installCommandForOps()
70
+ console.log(opsBanner(installCommand))
43
71
  } else {
44
72
  logger.info(`error: unknown command '${command}'`)
45
73
  }
@@ -1,4 +1,5 @@
1
1
  const fsx = require('./fsx')
2
+ const Errors = require('./errors')
2
3
 
3
4
  const RESERVED_ENV_FILES = ['.env.project', '.env.keys', '.env.me', '.env.x', '.env.example']
4
5
 
@@ -14,10 +15,7 @@ function findEnvFiles (directory) {
14
15
  return envFiles
15
16
  } catch (e) {
16
17
  if (e.code === 'ENOENT') {
17
- const error = new Error(`missing directory (${directory})`)
18
- error.code = 'MISSING_DIRECTORY'
19
-
20
- throw error
18
+ throw new Errors({ directory }).missingDirectory()
21
19
  } else {
22
20
  throw e
23
21
  }
@@ -1,5 +1,6 @@
1
1
  const fsx = require('./fsx')
2
2
  const path = require('path')
3
+ const Errors = require('./errors')
3
4
 
4
5
  const HOOK_SCRIPT = `#!/bin/sh
5
6
 
@@ -45,8 +46,7 @@ class InstallPrecommitHook {
45
46
  successMessage
46
47
  }
47
48
  } catch (err) {
48
- const error = new Error(`failed to modify pre-commit hook: ${err.message}`)
49
- throw error
49
+ throw new Errors({ error: err }).precommitHookModifyFailed()
50
50
  }
51
51
  }
52
52
 
package/src/lib/main.js CHANGED
@@ -81,16 +81,10 @@ const config = function (options = {}) {
81
81
 
82
82
  if (error.code === 'MISSING_ENV_FILE') {
83
83
  if (!options.convention) { // do not output error for conventions (too noisy)
84
- logger.error(error.message)
85
- if (error.help) {
86
- logger.error(error.help)
87
- }
84
+ logger.error(error.messageWithHelp)
88
85
  }
89
86
  } else {
90
- logger.error(error.message)
91
- if (error.help) {
92
- logger.error(error.help)
93
- }
87
+ logger.error(error.messageWithHelp)
94
88
  }
95
89
  }
96
90
 
@@ -127,10 +121,7 @@ const config = function (options = {}) {
127
121
  } catch (error) {
128
122
  if (strict) throw error // throw immediately if strict
129
123
 
130
- logger.error(error.message)
131
- if (error.help) {
132
- logger.help(error.help)
133
- }
124
+ logger.error(error.messageWithHelp)
134
125
 
135
126
  return { parsed: {}, error }
136
127
  }
@@ -154,10 +145,7 @@ const parse = function (src, options = {}) {
154
145
 
155
146
  // display any errors
156
147
  for (const error of errors) {
157
- logger.error(error.message)
158
- if (error.help) {
159
- logger.error(error.help)
160
- }
148
+ logger.error(error.messageWithHelp)
161
149
  }
162
150
 
163
151
  return parsed
@@ -199,15 +187,9 @@ const set = function (key, value, options = {}) {
199
187
  logger.verbose(`setting for ${processedEnv.envFilepath}`)
200
188
 
201
189
  if (processedEnv.error) {
202
- if (processedEnv.error.code === 'MISSING_ENV_FILE') {
203
- logger.warn(processedEnv.error.message)
204
- logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.envFilepath}] and re-run [dotenvx set]`)
205
- } else {
206
- logger.warn(processedEnv.error.message)
207
- if (processedEnv.error.help) {
208
- logger.help(processedEnv.error.help)
209
- }
210
- }
190
+ const error = processedEnv.error
191
+ const message = error.messageWithHelp || (error.help ? `${error.message}. ${error.help}` : error.message)
192
+ logger.warn(message)
211
193
  } else {
212
194
  fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
213
195
 
@@ -260,10 +242,7 @@ const get = function (key, options = {}) {
260
242
 
261
243
  if (options.strict) throw error // throw immediately if strict
262
244
 
263
- logger.error(error.message)
264
- if (error.help) {
265
- logger.error(error.help)
266
- }
245
+ logger.error(error.messageWithHelp)
267
246
  }
268
247
 
269
248
  if (key) {
@@ -122,7 +122,12 @@ class Encrypt {
122
122
  if (!encrypted) {
123
123
  row.keys.push(key) // track key(s)
124
124
 
125
- const encryptedValue = encryptValue(value, publicKey)
125
+ let encryptedValue
126
+ try {
127
+ encryptedValue = encryptValue(value, publicKey)
128
+ } catch {
129
+ throw new Errors({ publicKeyName, publicKey }).invalidPublicKey()
130
+ }
126
131
 
127
132
  // once newSrc is built write it out
128
133
  envSrc = replace(envSrc, key, encryptedValue)
@@ -17,14 +17,7 @@ class Genexample {
17
17
 
18
18
  run () {
19
19
  if (this.envFile.length < 1) {
20
- const code = 'MISSING_ENV_FILES'
21
- const message = 'no .env* files found'
22
- const help = '? add one with [echo "HELLO=World" > .env] and then run [dotenvx genexample]'
23
-
24
- const error = new Error(message)
25
- error.code = code
26
- error.help = help
27
- throw error
20
+ throw new Errors().missingEnvFiles()
28
21
  }
29
22
 
30
23
  const keys = new Set()
@@ -4,6 +4,7 @@ const path = require('path')
4
4
  const ignore = require('ignore')
5
5
 
6
6
  const Ls = require('../services/ls')
7
+ const Errors = require('../helpers/errors')
7
8
 
8
9
  const isFullyEncrypted = require('./../helpers/isFullyEncrypted')
9
10
  const packageJson = require('./../helpers/packageJson')
@@ -24,7 +25,10 @@ class Prebuild {
24
25
 
25
26
  // 1. check for .dockerignore file
26
27
  if (!fsx.existsSync('.dockerignore')) {
27
- const warning = new Error(`[dotenvx@${packageJson.version}][prebuild] .dockerignore missing`)
28
+ const warning = new Errors({
29
+ message: `[dotenvx@${packageJson.version}][prebuild] .dockerignore missing`,
30
+ help: 'fix: [touch .dockerignore]'
31
+ }).custom()
28
32
  warnings.push(warning)
29
33
  } else {
30
34
  dockerignore = fsx.readFileX('.dockerignore')
@@ -42,8 +46,10 @@ class Prebuild {
42
46
  // check if that file is being ignored
43
47
  if (ig.ignores(file)) {
44
48
  if (file === '.env.example' || file === '.env.x') {
45
- const warning = new Error(`[dotenvx@${packageJson.version}][prebuild] ${file} (currently ignored but should not be)`)
46
- warning.help = `[dotenvx@${packageJson.version}][prebuild] ⮕ run [dotenvx ext gitignore --pattern !${file}]`
49
+ const warning = new Errors({
50
+ message: `[dotenvx@${packageJson.version}][prebuild] ${file} (currently ignored but should not be)`,
51
+ help: `fix: [dotenvx ext gitignore --pattern !${file}]`
52
+ }).custom()
47
53
  warnings.push(warning)
48
54
  }
49
55
  } else {
@@ -54,15 +60,13 @@ class Prebuild {
54
60
  // if contents are encrypted don't raise an error
55
61
  if (!encrypted) {
56
62
  let errorMsg = `[dotenvx@${packageJson.version}][prebuild] ${file} not protected (encrypted or dockerignored)`
57
- let errorHelp = `[dotenvx@${packageJson.version}][prebuild] ⮕ run [dotenvx encrypt -f ${file}] or [dotenvx ext gitignore --pattern ${file}]`
63
+ let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx ext gitignore --pattern ${file}]`
58
64
  if (file.includes('.env.keys')) {
59
65
  errorMsg = `[dotenvx@${packageJson.version}][prebuild] ${file} not protected (dockerignored)`
60
- errorHelp = `[dotenvx@${packageJson.version}][prebuild] ⮕ run [dotenvx ext gitignore --pattern ${file}]`
66
+ errorHelp = `fix: [dotenvx ext gitignore --pattern ${file}]`
61
67
  }
62
68
 
63
- const error = new Error(errorMsg)
64
- error.help = errorHelp
65
- throw error
69
+ throw new Errors({ message: errorMsg, help: errorHelp }).custom()
66
70
  }
67
71
  }
68
72
  }
@@ -8,6 +8,7 @@ const Ls = require('../services/ls')
8
8
  const isFullyEncrypted = require('./../helpers/isFullyEncrypted')
9
9
  const packageJson = require('./../helpers/packageJson')
10
10
  const InstallPrecommitHook = require('./../helpers/installPrecommitHook')
11
+ const Errors = require('./../helpers/errors')
11
12
  const childProcess = require('child_process')
12
13
  const MISSING_GITIGNORE = '.env.keys' // by default only ignore .env.keys. all other .env* files COULD be included - as long as they are encrypted
13
14
 
@@ -37,7 +38,10 @@ class Precommit {
37
38
 
38
39
  // 1. check for .gitignore file
39
40
  if (!fsx.existsSync('.gitignore')) {
40
- const warning = new Error(`[dotenvx@${packageJson.version}][precommit] .gitignore missing`)
41
+ const warning = new Errors({
42
+ message: `[dotenvx@${packageJson.version}][precommit] .gitignore missing`,
43
+ help: 'fix: [touch .gitignore]'
44
+ }).custom()
41
45
  warnings.push(warning)
42
46
  } else {
43
47
  gitignore = fsx.readFileX('.gitignore')
@@ -58,8 +62,10 @@ class Precommit {
58
62
  // check if that file is being ignored
59
63
  if (ig.ignores(file)) {
60
64
  if (file === '.env.example' || file === '.env.x') {
61
- const warning = new Error(`[dotenvx@${packageJson.version}][precommit] ${file} (currently ignored but should not be)`)
62
- warning.help = `[dotenvx@${packageJson.version}][precommit] ⮕ run [dotenvx ext gitignore --pattern !${file}]`
65
+ const warning = new Errors({
66
+ message: `[dotenvx@${packageJson.version}][precommit] ${file} (currently ignored but should not be)`,
67
+ help: `fix: [dotenvx ext gitignore --pattern !${file}]`
68
+ }).custom()
63
69
  warnings.push(warning)
64
70
  }
65
71
  } else {
@@ -70,15 +76,13 @@ class Precommit {
70
76
  // if contents are encrypted don't raise an error
71
77
  if (!encrypted) {
72
78
  let errorMsg = `[dotenvx@${packageJson.version}][precommit] ${file} not protected (encrypted or gitignored)`
73
- let errorHelp = `[dotenvx@${packageJson.version}][precommit] ⮕ run [dotenvx encrypt -f ${file}] or [dotenvx ext gitignore --pattern ${file}]`
79
+ let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx ext gitignore --pattern ${file}]`
74
80
  if (file.includes('.env.keys')) {
75
81
  errorMsg = `[dotenvx@${packageJson.version}][precommit] ${file} not protected (gitignored)`
76
- errorHelp = `[dotenvx@${packageJson.version}][precommit] ⮕ run [dotenvx ext gitignore --pattern ${file}]`
82
+ errorHelp = `fix: [dotenvx ext gitignore --pattern ${file}]`
77
83
  }
78
84
 
79
- const error = new Error(errorMsg)
80
- error.help = errorHelp
81
- throw error
85
+ throw new Errors({ message: errorMsg, help: errorHelp }).custom()
82
86
  }
83
87
  }
84
88
  }
@@ -131,7 +131,12 @@ class Rotate {
131
131
  row.keys.push(key) // track key(s)
132
132
 
133
133
  const decryptedValue = decryptKeyValue(key, value, privateKeyName, privateKeyValue) // get decrypted value
134
- const encryptedValue = encryptValue(decryptedValue, newPublicKey) // encrypt with the new publicKey
134
+ let encryptedValue
135
+ try {
136
+ encryptedValue = encryptValue(decryptedValue, newPublicKey) // encrypt with the new publicKey
137
+ } catch {
138
+ throw new Errors({ publicKeyName, publicKey: newPublicKey }).invalidPublicKey()
139
+ }
135
140
 
136
141
  envSrc = replace(envSrc, key, encryptedValue)
137
142
  }
@@ -109,7 +109,11 @@ class Sets {
109
109
 
110
110
  row.publicKey = publicKey
111
111
  row.privateKey = privateKey
112
- row.encryptedValue = encryptValue(this.value, publicKey)
112
+ try {
113
+ row.encryptedValue = encryptValue(this.value, publicKey)
114
+ } catch {
115
+ throw new Errors({ publicKeyName, publicKey }).invalidPublicKey()
116
+ }
113
117
  row.privateKeyName = privateKeyName
114
118
  }
115
119
 
@@ -1,4 +1,5 @@
1
1
  const depth = require('../lib/helpers/colorDepth')
2
+ const Errors = require('../lib/helpers/errors')
2
3
 
3
4
  const colors16 = new Map([
4
5
  ['amber', 33],
@@ -6,7 +7,7 @@ const colors16 = new Map([
6
7
  ['gray', 37],
7
8
  ['green', 32],
8
9
  ['olive', 33],
9
- ['orangered', 31], // mapped to red
10
+ ['orangered', 33], // mapped to yellow/brown
10
11
  ['plum', 35], // mapped to magenta
11
12
  ['red', 31],
12
13
  ['electricblue', 36],
@@ -19,21 +20,23 @@ const colors256 = new Map([
19
20
  ['gray', 244],
20
21
  ['green', 34],
21
22
  ['olive', 142],
22
- ['orangered', 202],
23
+ ['orangered', 130], // burnished copper
23
24
  ['plum', 182],
24
- ['red', 196],
25
+ ['red', 124], // brighter garnet
25
26
  ['electricblue', 45],
26
27
  ['dodgerblue', 33]
27
28
  ])
28
29
 
29
30
  const colorsTrueColor = new Map([
30
- ['amber', [236, 213, 63]]
31
+ ['amber', [236, 213, 63]],
32
+ ['orangered', [138, 90, 43]], // #8A5A2B burnished copper
33
+ ['red', [140, 35, 50]] // #8C2332 brighter garnet
31
34
  ])
32
35
 
33
36
  function getColor (color) {
34
37
  const colorDepth = depth.getColorDepth()
35
38
  if (!colors256.has(color)) {
36
- throw new Error(`Invalid color ${color}`)
39
+ throw new Errors({ color }).invalidColor()
37
40
  }
38
41
  if (colorDepth >= 24 && colorsTrueColor.has(color)) {
39
42
  const [r, g, b] = colorsTrueColor.get(color)
@@ -1,4 +1,5 @@
1
1
  const packageJson = require('../lib/helpers/packageJson')
2
+ const Errors = require('../lib/helpers/errors')
2
3
  const { getColor, bold } = require('./colors')
3
4
 
4
5
  const levels = {
@@ -13,8 +14,8 @@ const levels = {
13
14
  silly: 6
14
15
  }
15
16
 
16
- const error = (m) => bold(getColor('red')(m))
17
- const warn = getColor('orangered')
17
+ const error = (m) => bold(getColor('red')(`☠ ${m}`))
18
+ const warn = (m) => getColor('orangered')(`⚠ ${m}`)
18
19
  const success = getColor('amber')
19
20
  const successv = getColor('amber')
20
21
  const info = getColor('gray')
@@ -33,7 +34,7 @@ function stderr (level, message) {
33
34
 
34
35
  function stdout (level, message) {
35
36
  if (levels[level] === undefined) {
36
- throw new Error(`MISSING_LOG_LEVEL: '${level}'. implement in logger.`)
37
+ throw new Errors({ level }).missingLogLevel()
37
38
  }
38
39
 
39
40
  if (levels[level] <= currentLevel) {