@dotenvx/dotenvx 0.16.1 → 0.17.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/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.16.1",
2
+ "version": "0.17.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a better dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -25,14 +25,14 @@
25
25
  "prerelease": "npm test",
26
26
  "release": "standard-version"
27
27
  },
28
- "funding": "Have you seen dotenvx.com? run anywhere, cross-platform, and encrypted envs.",
28
+ "funding": "https://dotenvx.com",
29
29
  "dependencies": {
30
30
  "@inquirer/prompts": "^3.3.0",
31
31
  "chalk": "^4.1.2",
32
32
  "clipboardy": "^2.3.0",
33
33
  "commander": "^11.1.0",
34
34
  "conf": "^10.2.0",
35
- "dotenv": "^16.4.5",
35
+ "dotenv": "16.4.5",
36
36
  "dotenv-expand": "^11.0.6",
37
37
  "execa": "^5.1.1",
38
38
  "glob": "^10.3.10",
@@ -1,4 +1,5 @@
1
1
  const fs = require('fs')
2
+ const path = require('path')
2
3
 
3
4
  const main = require('./../../lib/main')
4
5
  const logger = require('./../../shared/logger')
@@ -7,149 +8,76 @@ const createSpinner = require('./../../shared/createSpinner')
7
8
 
8
9
  const spinner = createSpinner('encrypting')
9
10
 
10
- // constants
11
- const ENCODING = 'utf8'
12
-
13
- async function encrypt () {
11
+ async function encrypt (directory) {
14
12
  spinner.start()
15
13
  await helpers.sleep(500) // better dx
16
14
 
15
+ logger.debug(`directory: ${directory}`)
16
+
17
17
  const options = this.opts()
18
18
  logger.debug(`options: ${JSON.stringify(options)}`)
19
19
 
20
- let optionEnvFile = options.envFile
21
- if (!Array.isArray(optionEnvFile)) {
22
- optionEnvFile = [optionEnvFile]
23
- }
24
-
25
- const addedKeys = new Set()
26
- const addedVaults = new Set()
27
- const addedEnvFilepaths = new Set()
28
-
29
- // must be at least one .env* file
30
- if (optionEnvFile.length < 1) {
31
- spinner.fail('no .env* files found')
32
- logger.help('? add one with [echo "HELLO=World" > .env] and then run [dotenvx encrypt]')
33
- process.exit(1)
34
- }
20
+ const optionEnvFile = options.envFile || helpers.findEnvFiles(directory)
35
21
 
36
22
  try {
37
- logger.verbose(`generating .env.keys from ${optionEnvFile}`)
38
-
39
- const dotenvKeys = (main.configDotenv({ path: '.env.keys' }).parsed || {})
40
-
41
- for (const envFilepath of optionEnvFile) {
42
- const filepath = helpers.resolvePath(envFilepath)
43
- if (!fs.existsSync(filepath)) {
44
- spinner.fail(`file does not exist at [${filepath}]`)
45
- logger.help(`? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx encrypt]`)
46
- process.exit(1)
47
- }
48
-
49
- const environment = helpers.guessEnvironment(filepath)
50
- const key = `DOTENV_KEY_${environment.toUpperCase()}`
23
+ const {
24
+ dotenvKeys,
25
+ dotenvKeysFile,
26
+ addedKeys,
27
+ existingKeys,
28
+ dotenvVaultFile,
29
+ addedVaults,
30
+ existingVaults,
31
+ addedDotenvFilenames
32
+ } = main.encrypt(directory, optionEnvFile)
51
33
 
52
- let value = dotenvKeys[key]
34
+ logger.verbose(`generating .env.keys from ${optionEnvFile}`)
35
+ if (addedKeys.length > 0) {
36
+ logger.verbose(`generated ${addedKeys}`)
37
+ }
38
+ if (existingKeys.length > 0) {
39
+ logger.verbose(`existing ${existingKeys}`)
40
+ }
41
+ fs.writeFileSync(path.resolve(directory, '.env.keys'), dotenvKeysFile)
53
42
 
54
- // first time seeing new DOTENV_KEY_${environment}
55
- if (!value || value.length === 0) {
56
- logger.verbose(`generating ${key}`)
57
- value = helpers.generateDotenvKey(environment)
58
- logger.debug(`generating ${key} as ${value}`)
43
+ logger.verbose(`generating .env.vault from ${optionEnvFile}`)
44
+ if (addedVaults.length > 0) {
45
+ logger.verbose(`encrypting ${addedVaults}`)
46
+ }
47
+ if (existingVaults.length > 0) {
48
+ logger.verbose(`existing ${existingVaults}`)
49
+ }
50
+ fs.writeFileSync(path.resolve(directory, '.env.vault'), dotenvVaultFile)
59
51
 
60
- dotenvKeys[key] = value
52
+ if (addedDotenvFilenames.length > 0) {
53
+ spinner.succeed(`encrypted to .env.vault (${addedDotenvFilenames})`)
54
+ logger.help2('ℹ commit .env.vault to code: [git commit -am ".env.vault"]')
55
+ } else {
56
+ spinner.done(`no changes (${optionEnvFile})`)
57
+ }
61
58
 
62
- addedKeys.add(key) // for info logging to user
63
- } else {
64
- logger.verbose(`existing ${key}`)
65
- logger.debug(`existing ${key} as ${value}`)
66
- }
59
+ if (addedKeys.length > 0) {
60
+ spinner.succeed(`${helpers.pluralize('key', addedKeys.length)} added to .env.keys (${addedKeys})`)
61
+ logger.help2('ℹ push .env.keys up to hub: [dotenvx hub push]')
67
62
  }
68
63
 
69
- let keysData = `#/!!!!!!!!!!!!!!!!!!!.env.keys!!!!!!!!!!!!!!!!!!!!!!/
70
- #/ DOTENV_KEYs. DO NOT commit to source control /
71
- #/ [how it works](https://dotenvx.com/env-keys) /
72
- #/--------------------------------------------------/\n`
64
+ if (addedVaults.length > 0) {
65
+ const DOTENV_VAULT_X = addedVaults[addedVaults.length - 1]
66
+ const DOTENV_KEY_X = DOTENV_VAULT_X.replace('_VAULT_', '_KEY_')
67
+ const tryKey = dotenvKeys[DOTENV_KEY_X] || '<dotenv_key_environment>'
73
68
 
74
- for (const key in dotenvKeys) {
75
- const value = dotenvKeys[key]
76
- keysData += `${key}="${value}"\n`
69
+ logger.help2(`ℹ run [DOTENV_KEY='${tryKey}' dotenvx run -- yourcommand] to test decryption locally`)
77
70
  }
78
-
79
- fs.writeFileSync('.env.keys', keysData)
80
71
  } catch (error) {
81
72
  spinner.fail(error.message)
82
- process.exit(1)
83
- }
84
-
85
- // used later in logging to user
86
- const dotenvKeys = (main.configDotenv({ path: '.env.keys' }).parsed || {})
87
-
88
- try {
89
- logger.verbose(`generating .env.vault from ${optionEnvFile}`)
90
-
91
- const dotenvVaults = (main.configDotenv({ path: '.env.vault' }).parsed || {})
92
-
93
- for (const envFilepath of optionEnvFile) {
94
- const filepath = helpers.resolvePath(envFilepath)
95
- const environment = helpers.guessEnvironment(filepath)
96
- const vault = `DOTENV_VAULT_${environment.toUpperCase()}`
97
-
98
- let ciphertext = dotenvVaults[vault]
99
- const dotenvKey = dotenvKeys[`DOTENV_KEY_${environment.toUpperCase()}`]
100
-
101
- if (!ciphertext || ciphertext.length === 0 || helpers.changed(ciphertext, dotenvKey, filepath, ENCODING)) {
102
- logger.verbose(`encrypting ${vault}`)
103
- ciphertext = helpers.encryptFile(filepath, dotenvKey, ENCODING)
104
- logger.verbose(`encrypting ${vault} as ${ciphertext}`)
105
-
106
- dotenvVaults[vault] = ciphertext
107
-
108
- addedVaults.add(vault) // for info logging to user
109
- addedEnvFilepaths.add(envFilepath) // for info logging to user
110
- } else {
111
- logger.verbose(`existing ${vault}`)
112
- logger.debug(`existing ${vault} as ${ciphertext}`)
113
- }
73
+ if (error.help) {
74
+ logger.help(error.help)
114
75
  }
115
-
116
- let vaultData = `#/-------------------.env.vault---------------------/
117
- #/ cloud-agnostic vaulting standard /
118
- #/ [how it works](https://dotenvx.com/env-vault) /
119
- #/--------------------------------------------------/\n\n`
120
-
121
- for (const vault in dotenvVaults) {
122
- const value = dotenvVaults[vault]
123
- const environment = vault.replace('DOTENV_VAULT_', '').toLowerCase()
124
- vaultData += `# ${environment}\n`
125
- vaultData += `${vault}="${value}"\n\n`
76
+ if (error.code) {
77
+ logger.debug(`ERROR_CODE: ${error.code}`)
126
78
  }
127
-
128
- fs.writeFileSync('.env.vault', vaultData)
129
- } catch (e) {
130
- spinner.fail(e.message)
131
79
  process.exit(1)
132
80
  }
133
-
134
- if (addedEnvFilepaths.size > 0) {
135
- spinner.succeed(`encrypted to .env.vault (${[...addedEnvFilepaths]})`)
136
- logger.help2('ℹ commit .env.vault to code: [git commit -am ".env.vault"]')
137
- } else {
138
- spinner.done(`no changes (${optionEnvFile})`)
139
- }
140
-
141
- if (addedKeys.size > 0) {
142
- spinner.succeed(`${helpers.pluralize('key', addedKeys.size)} added to .env.keys (${[...addedKeys]})`)
143
- logger.help2('ℹ push .env.keys up to hub: [dotenvx hub push]')
144
- }
145
-
146
- if (addedVaults.size > 0) {
147
- const DOTENV_VAULT_X = [...addedVaults][addedVaults.size - 1]
148
- const DOTENV_KEY_X = DOTENV_VAULT_X.replace('_VAULT_', '_KEY_')
149
- const tryKey = dotenvKeys[DOTENV_KEY_X] || '<dotenv_key_environment>'
150
-
151
- logger.help2(`ℹ run [DOTENV_KEY='${tryKey}' dotenvx run -- yourcommand] to test decryption locally`)
152
- }
153
81
  }
154
82
 
155
83
  module.exports = encrypt
@@ -5,7 +5,6 @@ const { Command } = require('commander')
5
5
  const program = new Command()
6
6
 
7
7
  const logger = require('./../shared/logger')
8
- const helpers = require('./helpers')
9
8
  const examples = require('./examples')
10
9
  const packageJson = require('./../shared/packageJson')
11
10
 
@@ -64,7 +63,8 @@ program.command('run')
64
63
  program.command('encrypt')
65
64
  .description('encrypt .env.* to .env.vault')
66
65
  .addHelpText('after', examples.encrypt)
67
- .option('-f, --env-file <paths...>', 'path(s) to your env file(s)', helpers.findEnvFiles('./'))
66
+ .argument('[directory]', 'directory to encrypt', '.')
67
+ .option('-f, --env-file <paths...>', 'path(s) to your env file(s)')
68
68
  .action(require('./actions/encrypt'))
69
69
 
70
70
  // dotenvx decrypt
@@ -0,0 +1,68 @@
1
+ const path = require('path')
2
+ const crypto = require('crypto')
3
+
4
+ class DotenvKeys {
5
+ constructor (envFilepaths = [], dotenvKeys = {}) {
6
+ this.envFilepaths = envFilepaths // pass .env* filepaths to be encrypted
7
+ this.dotenvKeys = dotenvKeys // pass current parsed dotenv keys from .env.keys file
8
+ }
9
+
10
+ run () {
11
+ const addedKeys = new Set()
12
+ const existingKeys = new Set()
13
+
14
+ for (const filepath of this.envFilepaths) {
15
+ const environment = this._guessEnvironment(filepath)
16
+ const key = `DOTENV_KEY_${environment.toUpperCase()}`
17
+
18
+ let value = this.dotenvKeys[key]
19
+
20
+ // first time seeing new DOTENV_KEY_${environment}
21
+ if (!value || value.length === 0) {
22
+ value = this._generateDotenvKey(environment)
23
+
24
+ this.dotenvKeys[key] = value
25
+
26
+ addedKeys.add(key) // for info logging to user
27
+ } else {
28
+ existingKeys.add(key) // for info logging to user
29
+ }
30
+ }
31
+
32
+ let keysData = `#/!!!!!!!!!!!!!!!!!!!.env.keys!!!!!!!!!!!!!!!!!!!!!!/
33
+ #/ DOTENV_KEYs. DO NOT commit to source control /
34
+ #/ [how it works](https://dotenvx.com/env-keys) /
35
+ #/--------------------------------------------------/\n`
36
+
37
+ for (const [key, value] of Object.entries(this.dotenvKeys)) {
38
+ keysData += `${key}="${value}"\n`
39
+ }
40
+
41
+ return {
42
+ dotenvKeys: this.dotenvKeys,
43
+ dotenvKeysFile: keysData,
44
+ addedKeys: [...addedKeys], // return set as array
45
+ existingKeys: [...existingKeys] // return set as array
46
+ }
47
+ }
48
+
49
+ _guessEnvironment (filepath) {
50
+ const filename = path.basename(filepath)
51
+ const parts = filename.split('.')
52
+ const possibleEnvironment = parts[2] // ['', 'env', environment', 'previous']
53
+
54
+ if (!possibleEnvironment || possibleEnvironment.length === 0) {
55
+ return 'development'
56
+ }
57
+
58
+ return possibleEnvironment
59
+ }
60
+
61
+ _generateDotenvKey (environment) {
62
+ const rand = crypto.randomBytes(32).toString('hex')
63
+
64
+ return `dotenv://:key_${rand}@dotenvx.com/vault/.env.vault?environment=${environment.toLowerCase()}`
65
+ }
66
+ }
67
+
68
+ module.exports = DotenvKeys
@@ -0,0 +1,151 @@
1
+ const path = require('path')
2
+ const crypto = require('crypto')
3
+ const xxhash = require('xxhashjs')
4
+ const dotenv = require('dotenv')
5
+
6
+ const XXHASH_SEED = 0xABCD
7
+ const NONCE_BYTES = 12
8
+
9
+ class DotenvVault {
10
+ constructor (dotenvFiles = {}, dotenvKeys = {}, dotenvVaults = {}) {
11
+ this.dotenvFiles = dotenvFiles // key: filepath and value: filecontent
12
+ this.dotenvKeys = dotenvKeys // pass current parsed dotenv keys from .env.keys
13
+ this.dotenvVaults = dotenvVaults // pass current parsed dotenv vaults from .env.vault
14
+ }
15
+
16
+ run () {
17
+ const addedVaults = new Set()
18
+ const existingVaults = new Set()
19
+ const addedDotenvFilenames = new Set()
20
+
21
+ for (const [filepath, raw] of Object.entries(this.dotenvFiles)) {
22
+ const environment = this._guessEnvironment(filepath)
23
+ const vault = `DOTENV_VAULT_${environment.toUpperCase()}`
24
+
25
+ let ciphertext = this.dotenvVaults[vault]
26
+ const dotenvKey = this.dotenvKeys[`DOTENV_KEY_${environment.toUpperCase()}`]
27
+
28
+ if (!ciphertext || ciphertext.length === 0 || this._changed(dotenvKey, ciphertext, raw)) {
29
+ ciphertext = this._encrypt(dotenvKey, raw)
30
+ this.dotenvVaults[vault] = ciphertext
31
+ addedVaults.add(vault) // for info logging to user
32
+
33
+ addedDotenvFilenames.add(path.basename(filepath)) // for info logging to user
34
+ } else {
35
+ existingVaults.add(vault) // for info logging to user
36
+ }
37
+ }
38
+
39
+ let vaultData = `#/-------------------.env.vault---------------------/
40
+ #/ cloud-agnostic vaulting standard /
41
+ #/ [how it works](https://dotenvx.com/env-vault) /
42
+ #/--------------------------------------------------/\n\n`
43
+
44
+ for (const [vault, value] of Object.entries(this.dotenvVaults)) {
45
+ const environment = vault.replace('DOTENV_VAULT_', '').toLowerCase()
46
+ vaultData += `# ${environment}\n`
47
+ vaultData += `${vault}="${value}"\n\n`
48
+ }
49
+
50
+ return {
51
+ dotenvVaultFile: vaultData,
52
+ addedVaults: [...addedVaults], // return set as array
53
+ existingVaults: [...existingVaults], // return set as array
54
+ addedDotenvFilenames: [...addedDotenvFilenames] // return set as array
55
+ }
56
+ }
57
+
58
+ _guessEnvironment (filepath) {
59
+ const filename = path.basename(filepath)
60
+ const parts = filename.split('.')
61
+ const possibleEnvironment = parts[2] // ['', 'env', environment', 'previous']
62
+
63
+ if (!possibleEnvironment || possibleEnvironment.length === 0) {
64
+ return 'development'
65
+ }
66
+
67
+ return possibleEnvironment
68
+ }
69
+
70
+ _changed (dotenvKey, ciphertext, raw) {
71
+ const decrypted = this._decrypt(dotenvKey, ciphertext)
72
+
73
+ return this._hash(decrypted) !== this._hash(raw)
74
+ }
75
+
76
+ _encrypt (dotenvKey, raw) {
77
+ const key = this._parseEncryptionKeyFromDotenvKey(dotenvKey)
78
+
79
+ // set up nonce
80
+ const nonce = crypto.randomBytes(NONCE_BYTES)
81
+
82
+ // set up cipher
83
+ const cipher = crypto.createCipheriv('aes-256-gcm', key, nonce)
84
+
85
+ // generate ciphertext
86
+ let ciphertext = ''
87
+ ciphertext += cipher.update(raw, 'utf8', 'hex')
88
+ ciphertext += cipher.final('hex')
89
+ ciphertext += cipher.getAuthTag().toString('hex')
90
+
91
+ // prepend nonce
92
+ ciphertext = nonce.toString('hex') + ciphertext
93
+
94
+ // base64 encode output
95
+ return Buffer.from(ciphertext, 'hex').toString('base64')
96
+ }
97
+
98
+ _decrypt (dotenvKey, ciphertext) {
99
+ const key = this._parseEncryptionKeyFromDotenvKey(dotenvKey)
100
+
101
+ try {
102
+ return dotenv.decrypt(ciphertext, key)
103
+ } catch (e) {
104
+ const decryptionFailedError = new Error('[DECRYPTION_FAILED] Unable to decrypt .env.vault with DOTENV_KEY.')
105
+ decryptionFailedError.code = 'DECRYPTION_FAILED'
106
+ decryptionFailedError.help = '[DECRYPTION_FAILED] Run with debug flag [dotenvx run --debug -- yourcommand] or manually run [echo $DOTENV_KEY] to compare it to the one in .env.keys.'
107
+ decryptionFailedError.debug = `[DECRYPTION_FAILED] DOTENV_KEY is ${dotenvKey}`
108
+
109
+ switch (e.code) {
110
+ case 'DECRYPTION_FAILED':
111
+ throw decryptionFailedError
112
+ default:
113
+ throw e
114
+ }
115
+ }
116
+ }
117
+
118
+ _parseEncryptionKeyFromDotenvKey (dotenvKey) {
119
+ // Parse DOTENV_KEY. Format is a URI
120
+ let uri
121
+ try {
122
+ uri = new URL(dotenvKey)
123
+ } catch (e) {
124
+ const code = 'INVALID_DOTENV_KEY'
125
+ const message = `INVALID_DOTENV_KEY: ${e.message}`
126
+ const error = new Error(message)
127
+ error.code = code
128
+
129
+ throw error
130
+ }
131
+
132
+ // Get decrypt key
133
+ const key = uri.password
134
+ if (!key) {
135
+ const code = 'INVALID_DOTENV_KEY'
136
+ const message = 'INVALID_DOTENV_KEY: Missing key part'
137
+ const error = new Error(message)
138
+ error.code = code
139
+
140
+ throw error
141
+ }
142
+
143
+ return Buffer.from(key.slice(-64), 'hex')
144
+ }
145
+
146
+ _hash (str) {
147
+ return xxhash.h32(str, XXHASH_SEED).toString(16)
148
+ }
149
+ }
150
+
151
+ module.exports = DotenvVault
package/src/lib/main.js CHANGED
@@ -3,6 +3,7 @@ const dotenv = require('dotenv')
3
3
  const dotenvExpand = require('dotenv-expand')
4
4
 
5
5
  // services
6
+ const Encrypt = require('./services/encrypt')
6
7
  const Ls = require('./services/ls')
7
8
 
8
9
  const config = function (options) {
@@ -105,6 +106,10 @@ const inject = function (processEnv = {}, parsed = {}, overload = false) {
105
106
  }
106
107
  }
107
108
 
109
+ const encrypt = function (directory, envFile) {
110
+ return new Encrypt(directory, envFile).run()
111
+ }
112
+
108
113
  const ls = function (directory, envFile) {
109
114
  return new Ls(directory, envFile).run()
110
115
  }
@@ -116,5 +121,6 @@ module.exports = {
116
121
  parse,
117
122
  parseExpand,
118
123
  inject,
119
- ls
124
+ ls,
125
+ encrypt
120
126
  }
@@ -0,0 +1,115 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const dotenv = require('dotenv')
4
+
5
+ const DotenvKeys = require('./../helpers/dotenvKeys')
6
+ const DotenvVault = require('./../helpers/dotenvVault')
7
+
8
+ const ENCODING = 'utf8'
9
+
10
+ class Encrypt {
11
+ constructor (directory = '.', envFile = '.env') {
12
+ this.directory = directory
13
+ this.envFile = envFile
14
+ // calculated
15
+ this.envKeysFilepath = path.resolve(this.directory, '.env.keys')
16
+ this.envVaultFilepath = path.resolve(this.directory, '.env.vault')
17
+ }
18
+
19
+ run () {
20
+ if (this.envFile.length < 1) {
21
+ const code = 'MISSING_ENV_FILES'
22
+ const message = 'no .env* files found'
23
+ const help = '? add one with [echo "HELLO=World" > .env] and then run [dotenvx encrypt]'
24
+
25
+ const error = new Error(message)
26
+ error.code = code
27
+ error.help = help
28
+ throw error
29
+ }
30
+
31
+ const parsedDotenvKeys = this._parsedDotenvKeys()
32
+ const parsedDotenvVaults = this._parsedDotenvVault()
33
+ const envFilepaths = this._envFilepaths()
34
+
35
+ // build filepaths to be passed to DotenvKeys
36
+ const uniqueEnvFilepaths = new Set()
37
+ for (const envFilepath of envFilepaths) {
38
+ const filepath = path.resolve(this.directory, envFilepath)
39
+ if (!fs.existsSync(filepath)) {
40
+ const code = 'MISSING_ENV_FILE'
41
+ const message = `file does not exist at [${filepath}]`
42
+ const help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx encrypt]`
43
+
44
+ const error = new Error(message)
45
+ error.code = code
46
+ error.help = help
47
+ throw error
48
+ }
49
+
50
+ uniqueEnvFilepaths.add(filepath)
51
+ }
52
+
53
+ // generate .env.keys string
54
+ const {
55
+ dotenvKeys,
56
+ dotenvKeysFile,
57
+ addedKeys,
58
+ existingKeys
59
+ } = new DotenvKeys([...uniqueEnvFilepaths], parsedDotenvKeys).run()
60
+
61
+ // build look up of .env filepaths and their raw content
62
+ const dotenvFiles = {}
63
+ for (const filepath of [...uniqueEnvFilepaths]) {
64
+ const raw = fs.readFileSync(filepath, ENCODING)
65
+ dotenvFiles[filepath] = raw
66
+ }
67
+
68
+ // generate .env.vault string
69
+ const {
70
+ dotenvVaultFile,
71
+ addedVaults,
72
+ existingVaults,
73
+ addedDotenvFilenames
74
+ } = new DotenvVault(dotenvFiles, dotenvKeys, parsedDotenvVaults).run()
75
+
76
+ return {
77
+ // from DotenvKeys
78
+ dotenvKeys,
79
+ dotenvKeysFile,
80
+ addedKeys,
81
+ existingKeys,
82
+ // from DotenvVault
83
+ dotenvVaultFile,
84
+ addedVaults,
85
+ existingVaults,
86
+ addedDotenvFilenames
87
+ }
88
+ }
89
+
90
+ _envFilepaths () {
91
+ if (!Array.isArray(this.envFile)) {
92
+ return [this.envFile]
93
+ }
94
+
95
+ return this.envFile
96
+ }
97
+
98
+ _parsedDotenvKeys () {
99
+ const options = {
100
+ path: this.envKeysFilepath
101
+ }
102
+
103
+ return dotenv.configDotenv(options).parsed
104
+ }
105
+
106
+ _parsedDotenvVault () {
107
+ const options = {
108
+ path: this.envVaultFilepath
109
+ }
110
+
111
+ return dotenv.configDotenv(options).parsed
112
+ }
113
+ }
114
+
115
+ module.exports = Encrypt