@dotenvx/dotenvx 1.59.0 → 1.60.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/README.md +130 -137
  3. package/package.json +7 -6
  4. package/src/cli/actions/decrypt.js +15 -8
  5. package/src/cli/actions/encrypt.js +15 -8
  6. package/src/cli/actions/ext/genexample.js +2 -2
  7. package/src/cli/actions/ext/gitignore.js +3 -3
  8. package/src/cli/actions/get.js +12 -7
  9. package/src/cli/actions/keypair.js +13 -5
  10. package/src/cli/actions/rotate.js +16 -9
  11. package/src/cli/actions/run.js +13 -6
  12. package/src/cli/actions/set.js +19 -12
  13. package/src/cli/dotenvx.js +19 -21
  14. package/src/cli/examples.js +1 -1
  15. package/src/db/session.js +10 -6
  16. package/src/lib/extensions/ops.js +112 -63
  17. package/src/lib/helpers/catchAndLog.js +1 -1
  18. package/src/lib/helpers/createSpinner.js +24 -0
  19. package/src/lib/helpers/cryptography/index.js +4 -0
  20. package/src/lib/helpers/cryptography/mutateKeysSrc.js +4 -4
  21. package/src/lib/helpers/cryptography/mutateKeysSrcSync.js +38 -0
  22. package/src/lib/helpers/cryptography/opsKeypair.js +2 -2
  23. package/src/lib/helpers/cryptography/opsKeypairSync.js +14 -0
  24. package/src/lib/helpers/cryptography/provision.js +7 -7
  25. package/src/lib/helpers/cryptography/provisionSync.js +47 -0
  26. package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +1 -1
  27. package/src/lib/helpers/detectEncoding.js +2 -2
  28. package/src/lib/helpers/detectEncodingSync.js +22 -0
  29. package/src/lib/helpers/errors.js +15 -0
  30. package/src/lib/helpers/fsx.js +27 -3
  31. package/src/lib/helpers/installPrecommitHook.js +2 -2
  32. package/src/lib/helpers/isIgnoringDotenvKeys.js +1 -1
  33. package/src/lib/helpers/keyResolution/index.js +3 -1
  34. package/src/lib/helpers/keyResolution/keyValues.js +12 -12
  35. package/src/lib/helpers/keyResolution/keyValuesSync.js +85 -0
  36. package/src/lib/helpers/keyResolution/readFileKey.js +10 -8
  37. package/src/lib/helpers/keyResolution/readFileKeySync.js +15 -0
  38. package/src/lib/helpers/kits/sample.js +11 -21
  39. package/src/lib/main.d.ts +18 -3
  40. package/src/lib/main.js +18 -18
  41. package/src/lib/services/decrypt.js +8 -8
  42. package/src/lib/services/encrypt.js +17 -11
  43. package/src/lib/services/genexample.js +2 -2
  44. package/src/lib/services/get.js +30 -21
  45. package/src/lib/services/keypair.js +21 -5
  46. package/src/lib/services/prebuild.js +7 -12
  47. package/src/lib/services/precommit.js +7 -11
  48. package/src/lib/services/rotate.js +22 -18
  49. package/src/lib/services/run.js +82 -9
  50. package/src/lib/services/sets.js +139 -12
  51. package/src/shared/logger.js +3 -3
  52. package/src/lib/helpers/sleep.js +0 -5
package/src/db/session.js CHANGED
@@ -1,20 +1,24 @@
1
1
  const Ops = require('./../lib/extensions/ops')
2
+ const { logger } = require('./../shared/logger')
2
3
 
3
4
  class Session {
4
5
  constructor () {
5
6
  this.ops = new Ops()
6
- this.opsStatus = this.ops.status()
7
7
  }
8
8
 
9
9
  //
10
- // opsOff/On
10
+ // ops status helpers
11
11
  //
12
- opsOn () {
13
- return this.opsStatus === 'on'
12
+ async noOps () {
13
+ const status = await this.ops.status()
14
+ logger.debug(`ops: ${status}`)
15
+ return status === 'off'
14
16
  }
15
17
 
16
- opsOff () {
17
- return !this.opsOn()
18
+ noOpsSync () {
19
+ const status = this.ops.statusSync()
20
+ logger.debug(`ops: ${status}`)
21
+ return status === 'off'
18
22
  }
19
23
  }
20
24
 
@@ -1,97 +1,146 @@
1
1
  const path = require('path')
2
2
  const childProcess = require('child_process')
3
+ const util = require('util')
3
4
 
4
- // const { logger } = require('./../../shared/logger')
5
+ const execFile = util.promisify(childProcess.execFile)
5
6
 
6
7
  class Ops {
7
- constructor () {
8
- this.opsLib = null
8
+ async status () {
9
+ if (this._isForcedOff()) return 'off'
9
10
 
10
- if (this._isForcedOff()) {
11
- return
11
+ const binary = await this._resolveBinary()
12
+ if (!binary) return 'off'
13
+
14
+ try {
15
+ return await this._exec(binary, ['status'])
16
+ } catch (_e) {
17
+ return 'off'
12
18
  }
19
+ }
13
20
 
14
- // check npm lib
15
- try { this.opsLib = this._opsNpm() } catch (_e) {}
21
+ statusSync () {
22
+ if (this._isForcedOff()) return 'off'
16
23
 
17
- // check binary cli
18
- if (!this.opsLib) {
19
- try { this.opsLib = this._opsCli() } catch (_e) {}
20
- }
24
+ const binary = this._resolveBinarySync()
25
+ if (!binary) return 'off'
21
26
 
22
- if (this.opsLib) {
23
- // logger.successv(`⛨ ops: ${this.opsLib.status()}`)
27
+ try {
28
+ return this._execSync(binary, ['status'])
29
+ } catch (_e) {
30
+ return 'off'
24
31
  }
25
32
  }
26
33
 
27
- status () {
28
- if (this._isForcedOff() || !this.opsLib) {
29
- return 'off'
30
- }
34
+ async keypair (publicKey) {
35
+ if (this._isForcedOff()) return {}
31
36
 
32
- return this.opsLib.status()
33
- }
37
+ const binary = await this._resolveBinary()
38
+ if (!binary) return {}
39
+
40
+ const args = ['keypair']
41
+ if (publicKey) args.push(publicKey)
34
42
 
35
- keypair (publicKey) {
36
- if (this._isForcedOff() || !this.opsLib) {
43
+ try {
44
+ return JSON.parse(await this._exec(binary, args))
45
+ } catch (_e) {
37
46
  return {}
38
47
  }
48
+ }
49
+
50
+ keypairSync (publicKey) {
51
+ if (this._isForcedOff()) return {}
39
52
 
40
- return this.opsLib.keypair(publicKey)
53
+ const binary = this._resolveBinarySync()
54
+ if (!binary) return {}
55
+
56
+ const args = ['keypair']
57
+ if (publicKey) args.push(publicKey)
58
+
59
+ try {
60
+ return JSON.parse(this._execSync(binary, args))
61
+ } catch (_e) {
62
+ return {}
63
+ }
41
64
  }
42
65
 
43
66
  observe (payload) {
44
- if (!this._isForcedOff() && this.opsLib && this.opsLib.status() !== 'off') {
45
- const encoded = Buffer.from(JSON.stringify(payload)).toString('base64')
46
- this.opsLib.observe(encoded)
67
+ if (this._isForcedOff()) return
68
+
69
+ const binary = this._resolveBinarySync()
70
+ if (!binary) return
71
+
72
+ let status = 'off'
73
+ try {
74
+ status = this._execSync(binary, ['status'])
75
+ } catch (_e) {
76
+ return
77
+ }
78
+ if (status === 'off') return
79
+
80
+ const encoded = Buffer.from(JSON.stringify(payload)).toString('base64')
81
+ try {
82
+ const subprocess = childProcess.spawn(binary, ['observe', encoded], {
83
+ stdio: 'ignore',
84
+ detached: true
85
+ })
86
+ subprocess.unref()
87
+ } catch (_e) {
88
+ // noop
47
89
  }
48
90
  }
49
91
 
50
- //
51
- // private
52
- //
53
- _opsNpm () {
54
- const npmBin = path.resolve(process.cwd(), 'node_modules/.bin/dotenvx-ops')
55
- return this._opsLib(npmBin)
92
+ async _exec (binary, args) {
93
+ const { stdout } = await execFile(binary, args)
94
+ return stdout.toString().trim()
56
95
  }
57
96
 
58
- _opsCli () {
59
- return this._opsLib('dotenvx-ops')
97
+ _execSync (binary, args) {
98
+ return childProcess.execFileSync(binary, args).toString().trim()
60
99
  }
61
100
 
62
- _opsLib (binary) {
63
- childProcess.execFileSync(binary, ['--version'], { stdio: ['pipe', 'pipe', 'ignore'] })
64
-
65
- return {
66
- status: () => {
67
- return childProcess.execFileSync(binary, ['status'], { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
68
- },
69
- keypair: (publicKey) => {
70
- const args = ['keypair']
71
- if (publicKey) {
72
- args.push(publicKey)
73
- }
74
- const output = childProcess.execFileSync(binary, args, { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
75
- const parsed = JSON.parse(output.toString())
76
- return parsed
77
- },
78
- observe: (encoded) => {
79
- try {
80
- const subprocess = childProcess.spawn(binary, ['observe', encoded], {
81
- stdio: 'ignore',
82
- detached: true
83
- })
84
-
85
- subprocess.unref() // let it run independently
86
- } catch (e) {
87
- // noop
88
- }
89
- }
90
- }
101
+ async _resolveBinary () {
102
+ if (this._binaryPromise) return this._binaryPromise
103
+
104
+ this._binaryPromise = (async () => {
105
+ const npmBin = path.resolve(process.cwd(), 'node_modules/.bin/dotenvx-ops')
106
+ try {
107
+ await this._exec(npmBin, ['--version'])
108
+ return npmBin
109
+ } catch (_e) {}
110
+
111
+ try {
112
+ await this._exec('dotenvx-ops', ['--version'])
113
+ return 'dotenvx-ops'
114
+ } catch (_e) {}
115
+
116
+ return null
117
+ })()
118
+
119
+ return this._binaryPromise
120
+ }
121
+
122
+ _resolveBinarySync () {
123
+ if (this._binarySync !== undefined) return this._binarySync
124
+
125
+ const npmBin = path.resolve(process.cwd(), 'node_modules/.bin/dotenvx-ops')
126
+ try {
127
+ this._execSync(npmBin, ['--version'])
128
+ this._binarySync = npmBin
129
+ return this._binarySync
130
+ } catch (_e) {}
131
+
132
+ try {
133
+ this._execSync('dotenvx-ops', ['--version'])
134
+ this._binarySync = 'dotenvx-ops'
135
+ return this._binarySync
136
+ } catch (_e) {}
137
+
138
+ this._binarySync = null
139
+ return null
91
140
  }
92
141
 
93
142
  _isForcedOff () {
94
- return process.env.DOTENVX_OPS_OFF === 'true'
143
+ return process.env.DOTENVX_NO_OPS === 'true'
95
144
  }
96
145
  }
97
146
 
@@ -1,7 +1,7 @@
1
1
  const { logger } = require('./../../shared/logger')
2
2
 
3
3
  function catchAndLog (error) {
4
- logger.error(error.messageWithHelp)
4
+ logger.error(error.messageWithHelp || error.message)
5
5
  if (error.debug) {
6
6
  logger.debug(error.debug)
7
7
  }
@@ -0,0 +1,24 @@
1
+ const FRAMES = ['◇', '⬖', '◆', '⬗']
2
+ const FRAME_INTERVAL_MS = 80
3
+
4
+ async function createSpinner (options = {}) {
5
+ const stream = process.stderr
6
+ const hasCursorControls = typeof stream.cursorTo === 'function' && typeof stream.clearLine === 'function'
7
+ const enabled = Boolean(stream.isTTY && hasCursorControls && !options.quiet && !options.verbose && !options.debug)
8
+ if (!enabled) return null
9
+
10
+ const text = options.text || 'thinking'
11
+ const frames = options.frames || FRAMES
12
+
13
+ const { default: yoctoSpinner } = await import('yocto-spinner')
14
+ return yoctoSpinner({
15
+ text,
16
+ spinner: {
17
+ frames,
18
+ interval: FRAME_INTERVAL_MS
19
+ },
20
+ stream
21
+ }).start()
22
+ }
23
+
24
+ module.exports = createSpinner
@@ -1,11 +1,15 @@
1
1
  module.exports = {
2
2
  opsKeypair: require('./opsKeypair'),
3
+ opsKeypairSync: require('./opsKeypairSync'),
3
4
  localKeypair: require('./localKeypair'),
4
5
  encryptValue: require('./encryptValue'),
5
6
  decryptKeyValue: require('./decryptKeyValue'),
6
7
  isEncrypted: require('./isEncrypted'),
7
8
  isPublicKey: require('./isPublicKey'),
9
+ mutateKeysSrc: require('./mutateKeysSrc'),
10
+ mutateKeysSrcSync: require('./mutateKeysSrcSync'),
8
11
  provision: require('./provision'),
12
+ provisionSync: require('./provisionSync'),
9
13
  provisionWithPrivateKey: require('./provisionWithPrivateKey'),
10
14
  mutateSrc: require('./mutateSrc'),
11
15
 
@@ -9,7 +9,7 @@ const FIRST_TIME_KEYS_SRC = [
9
9
  const path = require('path')
10
10
  const fsx = require('./../fsx')
11
11
 
12
- function mutateKeysSrc ({ envFilepath, keysFilepath, privateKeyName, privateKeyValue }) {
12
+ async function mutateKeysSrc ({ envFilepath, keysFilepath, privateKeyName, privateKeyValue }) {
13
13
  const filename = path.basename(envFilepath)
14
14
  const filepath = path.resolve(envFilepath)
15
15
  let resolvedKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
@@ -19,13 +19,13 @@ function mutateKeysSrc ({ envFilepath, keysFilepath, privateKeyName, privateKeyV
19
19
  const appendPrivateKey = [`# ${filename}`, `${privateKeyName}=${privateKeyValue}`, ''].join('\n')
20
20
 
21
21
  let keysSrc = ''
22
- if (fsx.existsSync(resolvedKeysFilepath)) {
23
- keysSrc = fsx.readFileX(resolvedKeysFilepath)
22
+ if (await fsx.exists(resolvedKeysFilepath)) {
23
+ keysSrc = await fsx.readFileX(resolvedKeysFilepath)
24
24
  }
25
25
  keysSrc = keysSrc.length > 1 ? keysSrc : `${FIRST_TIME_KEYS_SRC}\n`
26
26
  keysSrc = `${keysSrc}\n${appendPrivateKey}`
27
27
 
28
- fsx.writeFileX(resolvedKeysFilepath, keysSrc) // TODO: don't write if ops
28
+ await fsx.writeFileX(resolvedKeysFilepath, keysSrc) // TODO: don't write if ops
29
29
 
30
30
  const envKeysFilepath = keysFilepath || path.join(path.dirname(envFilepath), path.basename(resolvedKeysFilepath))
31
31
 
@@ -0,0 +1,38 @@
1
+ const FIRST_TIME_KEYS_SRC = [
2
+ '#/------------------!DOTENV_PRIVATE_KEYS!-------------------/',
3
+ '#/ private decryption keys. DO NOT commit to source control /',
4
+ '#/ [how it works](https://dotenvx.com/encryption) /',
5
+ // '#/ backup with: `dotenvx ops backup` /',
6
+ '#/----------------------------------------------------------/'
7
+ ].join('\n')
8
+
9
+ const path = require('path')
10
+ const fsx = require('./../fsx')
11
+
12
+ function mutateKeysSrcSync ({ envFilepath, keysFilepath, privateKeyName, privateKeyValue }) {
13
+ const filename = path.basename(envFilepath)
14
+ const filepath = path.resolve(envFilepath)
15
+ let resolvedKeysFilepath = path.join(path.dirname(filepath), '.env.keys')
16
+ if (keysFilepath) {
17
+ resolvedKeysFilepath = path.resolve(keysFilepath)
18
+ }
19
+ const appendPrivateKey = [`# ${filename}`, `${privateKeyName}=${privateKeyValue}`, ''].join('\n')
20
+
21
+ let keysSrc = ''
22
+ if (fsx.existsSync(resolvedKeysFilepath)) {
23
+ keysSrc = fsx.readFileXSync(resolvedKeysFilepath)
24
+ }
25
+ keysSrc = keysSrc.length > 1 ? keysSrc : `${FIRST_TIME_KEYS_SRC}\n`
26
+ keysSrc = `${keysSrc}\n${appendPrivateKey}`
27
+
28
+ fsx.writeFileXSync(resolvedKeysFilepath, keysSrc) // TODO: don't write if ops
29
+
30
+ const envKeysFilepath = keysFilepath || path.join(path.dirname(envFilepath), path.basename(resolvedKeysFilepath))
31
+
32
+ return {
33
+ keysSrc,
34
+ envKeysFilepath
35
+ }
36
+ }
37
+
38
+ module.exports = mutateKeysSrcSync
@@ -1,7 +1,7 @@
1
1
  const Ops = require('../../extensions/ops')
2
2
 
3
- function opsKeypair (existingPublicKey) {
4
- const kp = new Ops().keypair(existingPublicKey)
3
+ async function opsKeypair (existingPublicKey) {
4
+ const kp = await new Ops().keypair(existingPublicKey)
5
5
  const publicKey = kp.public_key
6
6
  const privateKey = kp.private_key
7
7
 
@@ -0,0 +1,14 @@
1
+ const Ops = require('../../extensions/ops')
2
+
3
+ function opsKeypairSync (existingPublicKey) {
4
+ const kp = new Ops().keypairSync(existingPublicKey)
5
+ const publicKey = kp.public_key
6
+ const privateKey = kp.private_key
7
+
8
+ return {
9
+ publicKey,
10
+ privateKey
11
+ }
12
+ }
13
+
14
+ module.exports = opsKeypairSync
@@ -4,8 +4,8 @@ const opsKeypair = require('./opsKeypair')
4
4
  const localKeypair = require('./localKeypair')
5
5
  const { keyNames } = require('../keyResolution')
6
6
 
7
- function provision ({ envSrc, envFilepath, keysFilepath, opsOn }) {
8
- opsOn = opsOn === true
7
+ async function provision ({ envSrc, envFilepath, keysFilepath, noOps }) {
8
+ noOps = noOps !== false
9
9
  const { publicKeyName, privateKeyName } = keyNames(envFilepath)
10
10
 
11
11
  let publicKey
@@ -14,12 +14,12 @@ function provision ({ envSrc, envFilepath, keysFilepath, opsOn }) {
14
14
  let envKeysFilepath
15
15
  let privateKeyAdded = false
16
16
 
17
- if (opsOn) {
18
- const kp = opsKeypair()
17
+ if (noOps) {
18
+ const kp = localKeypair()
19
19
  publicKey = kp.publicKey
20
20
  privateKey = kp.privateKey
21
21
  } else {
22
- const kp = localKeypair()
22
+ const kp = await opsKeypair()
23
23
  publicKey = kp.publicKey
24
24
  privateKey = kp.privateKey
25
25
  }
@@ -27,8 +27,8 @@ function provision ({ envSrc, envFilepath, keysFilepath, opsOn }) {
27
27
  const mutated = mutateSrc({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue: publicKey })
28
28
  envSrc = mutated.envSrc
29
29
 
30
- if (!opsOn) {
31
- const mutated = mutateKeysSrc({ envFilepath, keysFilepath, privateKeyName, privateKeyValue: privateKey })
30
+ if (noOps) {
31
+ const mutated = await mutateKeysSrc({ envFilepath, keysFilepath, privateKeyName, privateKeyValue: privateKey })
32
32
  keysSrc = mutated.keysSrc
33
33
  envKeysFilepath = mutated.envKeysFilepath
34
34
  privateKeyAdded = true
@@ -0,0 +1,47 @@
1
+ const mutateSrc = require('./mutateSrc')
2
+ const mutateKeysSrcSync = require('./mutateKeysSrcSync')
3
+ const opsKeypairSync = require('./opsKeypairSync')
4
+ const localKeypair = require('./localKeypair')
5
+ const { keyNames } = require('../keyResolution')
6
+
7
+ function provisionSync ({ envSrc, envFilepath, keysFilepath, noOps }) {
8
+ noOps = noOps !== false
9
+ const { publicKeyName, privateKeyName } = keyNames(envFilepath)
10
+
11
+ let publicKey
12
+ let privateKey
13
+ let keysSrc
14
+ let envKeysFilepath
15
+ let privateKeyAdded = false
16
+
17
+ if (noOps) {
18
+ const kp = localKeypair()
19
+ publicKey = kp.publicKey
20
+ privateKey = kp.privateKey
21
+ } else {
22
+ const kp = opsKeypairSync()
23
+ publicKey = kp.publicKey
24
+ privateKey = kp.privateKey
25
+ }
26
+
27
+ const mutated = mutateSrc({ envSrc, envFilepath, keysFilepath, publicKeyName, publicKeyValue: publicKey })
28
+ envSrc = mutated.envSrc
29
+
30
+ if (noOps) {
31
+ const mutated = mutateKeysSrcSync({ envFilepath, keysFilepath, privateKeyName, privateKeyValue: privateKey })
32
+ keysSrc = mutated.keysSrc
33
+ envKeysFilepath = mutated.envKeysFilepath
34
+ privateKeyAdded = true
35
+ }
36
+
37
+ return {
38
+ envSrc,
39
+ keysSrc,
40
+ publicKey,
41
+ privateKey,
42
+ privateKeyAdded,
43
+ envKeysFilepath
44
+ }
45
+ }
46
+
47
+ module.exports = provisionSync
@@ -3,7 +3,7 @@ const mutateSrc = require('./mutateSrc')
3
3
  const localKeypair = require('./localKeypair')
4
4
 
5
5
  function provisionWithPrivateKey ({ envSrc, envFilepath, keysFilepath, privateKeyValue, publicKeyValue, publicKeyName }) {
6
- const { publicKey, privateKey } = localKeypair(privateKeyValue) // opsOn doesn't matter here since privateKeyValue was already discovered prior (via ops and local) and passed as privateKeyValue
6
+ const { publicKey, privateKey } = localKeypair(privateKeyValue) // noOps doesn't matter here since privateKeyValue was already discovered prior (via ops and local) and passed as privateKeyValue
7
7
 
8
8
  // if derivation doesn't match what's in the file (or preset in env)
9
9
  if (publicKeyValue && publicKeyValue !== publicKey) {
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs')
2
2
 
3
- function detectEncoding (filepath) {
4
- const buffer = fs.readFileSync(filepath)
3
+ async function detectEncoding (filepath) {
4
+ const buffer = await fs.promises.readFile(filepath)
5
5
 
6
6
  // check for UTF-16LE BOM (Byte Order Mark)
7
7
  if (buffer.length >= 2 && buffer[0] === 0xFF && buffer[1] === 0xFE) {
@@ -0,0 +1,22 @@
1
+ const fs = require('fs')
2
+
3
+ function detectEncodingSync (filepath) {
4
+ const buffer = fs.readFileSync(filepath)
5
+
6
+ // check for UTF-16LE BOM (Byte Order Mark)
7
+ if (buffer.length >= 2 && buffer[0] === 0xFF && buffer[1] === 0xFE) {
8
+ return 'utf16le'
9
+ }
10
+
11
+ /* c8 ignore start */
12
+ // check for UTF-8 BOM
13
+ if (buffer.length >= 3 && buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) {
14
+ return 'utf8'
15
+ }
16
+
17
+ /* c8 ignore stop */
18
+
19
+ return 'utf8'
20
+ }
21
+
22
+ module.exports = detectEncodingSync
@@ -13,6 +13,7 @@ const ISSUE_BY_CODE = {
13
13
  MISPAIRED_PRIVATE_KEY: 'https://github.com/dotenvx/dotenvx/issues/752',
14
14
  MISSING_DIRECTORY: 'https://github.com/dotenvx/dotenvx/issues/758',
15
15
  MISSING_ENV_FILE: 'https://github.com/dotenvx/dotenvx/issues/484',
16
+ MISSING_ENV_KEYS_FILE: 'https://github.com/dotenvx/dotenvx/issues/775',
16
17
  MISSING_ENV_FILES: 'https://github.com/dotenvx/dotenvx/issues/760',
17
18
  MISSING_KEY: 'https://github.com/dotenvx/dotenvx/issues/759',
18
19
  MISSING_LOG_LEVEL: 'must be valid log level',
@@ -25,6 +26,7 @@ class Errors {
25
26
  constructor (options = {}) {
26
27
  this.filepath = options.filepath
27
28
  this.envFilepath = options.envFilepath
29
+ this.envKeysFilepath = options.envKeysFilepath
28
30
 
29
31
  this.key = options.key
30
32
  this.privateKey = options.privateKey
@@ -202,6 +204,19 @@ class Errors {
202
204
  return e
203
205
  }
204
206
 
207
+ missingEnvKeysFile () {
208
+ const code = 'MISSING_ENV_KEYS_FILE'
209
+ const envKeysFilepath = this.envKeysFilepath || '.env.keys'
210
+ const message = `[${code}] missing file (${envKeysFilepath})`
211
+ const help = `fix: [${ISSUE_BY_CODE[code]}]`
212
+
213
+ const e = new Error(message)
214
+ e.code = code
215
+ e.help = help
216
+ e.messageWithHelp = `${message}. ${help}`
217
+ return e
218
+ }
219
+
205
220
  missingEnvFiles () {
206
221
  const code = 'MISSING_ENV_FILES'
207
222
  const message = `[${code}] no .env* files found`
@@ -2,7 +2,15 @@ const fs = require('fs')
2
2
 
3
3
  const ENCODING = 'utf8'
4
4
 
5
- function readFileX (filepath, encoding = null) {
5
+ async function readFileX (filepath, encoding = null) {
6
+ if (!encoding) {
7
+ encoding = ENCODING
8
+ }
9
+
10
+ return fs.promises.readFile(filepath, encoding)
11
+ }
12
+
13
+ function readFileXSync (filepath, encoding = null) {
6
14
  if (!encoding) {
7
15
  encoding = ENCODING
8
16
  }
@@ -10,12 +18,26 @@ function readFileX (filepath, encoding = null) {
10
18
  return fs.readFileSync(filepath, encoding) // utf8 default so it returns a string
11
19
  }
12
20
 
13
- function writeFileX (filepath, str) {
21
+ function writeFileXSync (filepath, str) {
14
22
  return fs.writeFileSync(filepath, str, ENCODING) // utf8 always
15
23
  }
16
24
 
25
+ async function writeFileX (filepath, str) {
26
+ return fs.promises.writeFile(filepath, str, ENCODING)
27
+ }
28
+
29
+ async function exists (filepath) {
30
+ try {
31
+ await fs.promises.access(filepath)
32
+ return true
33
+ } catch (_e) {
34
+ return false
35
+ }
36
+ }
37
+
17
38
  const fsx = {
18
39
  chmodSync: fs.chmodSync,
40
+ exists,
19
41
  existsSync: fs.existsSync,
20
42
  readdirSync: fs.readdirSync,
21
43
  readFileSync: fs.readFileSync,
@@ -24,7 +46,9 @@ const fsx = {
24
46
 
25
47
  // fsx special commands
26
48
  readFileX,
27
- writeFileX
49
+ readFileXSync,
50
+ writeFileX,
51
+ writeFileXSync
28
52
  }
29
53
 
30
54
  module.exports = fsx
@@ -55,12 +55,12 @@ class InstallPrecommitHook {
55
55
  }
56
56
 
57
57
  _currentHook () {
58
- return fsx.readFileX(this.hookPath)
58
+ return fsx.readFileXSync(this.hookPath)
59
59
  }
60
60
 
61
61
  _createHook () {
62
62
  // If the pre-commit file doesn't exist, create a new one with the hookScript
63
- fsx.writeFileX(this.hookPath, HOOK_SCRIPT)
63
+ fsx.writeFileXSync(this.hookPath, HOOK_SCRIPT)
64
64
  fsx.chmodSync(this.hookPath, '755') // Make the file executable
65
65
  }
66
66
 
@@ -6,7 +6,7 @@ function isIgnoringDotenvKeys () {
6
6
  return false
7
7
  }
8
8
 
9
- const gitignore = fsx.readFileX('.gitignore')
9
+ const gitignore = fsx.readFileXSync('.gitignore')
10
10
  const ig = ignore(gitignore).add(gitignore)
11
11
 
12
12
  if (!ig.ignores('.env.keys')) {
@@ -1,13 +1,15 @@
1
1
  module.exports = {
2
2
  keyNames: require('./keyNames'),
3
3
  keyValues: require('./keyValues'),
4
+ keyValuesSync: require('./keyValuesSync'),
4
5
 
5
6
  // private
6
- // private keys are resolved via keyValues()
7
+ // private keys are resolved via keyValuesSync()
7
8
 
8
9
  // other
9
10
  readProcessKey: require('./readProcessKey'),
10
11
  readFileKey: require('./readFileKey'),
12
+ readFileKeySync: require('./readFileKeySync'),
11
13
  guessPrivateKeyFilename: require('./../guessPrivateKeyFilename'),
12
14
  dotenvPrivateKeyNames: require('./../dotenvPrivateKeyNames')
13
15
  }