@dotenvx/dotenvx 1.59.1 → 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 +12 -1
  2. package/README.md +14 -4
  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 +9 -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
@@ -11,7 +11,8 @@ const {
11
11
 
12
12
  const {
13
13
  keyNames,
14
- keyValues
14
+ keyValues,
15
+ keyValuesSync
15
16
  } = require('./../helpers/keyResolution')
16
17
 
17
18
  const {
@@ -19,21 +20,23 @@ const {
19
20
  decryptKeyValue,
20
21
  isEncrypted,
21
22
  provision,
23
+ provisionSync,
22
24
  provisionWithPrivateKey
23
25
  } = require('./../helpers/cryptography')
24
26
 
25
27
  const replace = require('./../helpers/replace')
26
28
  const dotenvParse = require('./../helpers/dotenvParse')
27
29
  const detectEncoding = require('./../helpers/detectEncoding')
30
+ const detectEncodingSync = require('./../helpers/detectEncodingSync')
28
31
 
29
32
  class Sets {
30
- constructor (key, value, envs = [], encrypt = true, envKeysFilepath = null, opsOn = false, noCreate = false) {
33
+ constructor (key, value, envs = [], encrypt = true, envKeysFilepath = null, noOps = false, noCreate = false) {
31
34
  this.envs = determine(envs, process.env)
32
35
  this.key = key
33
36
  this.value = value
34
37
  this.encrypt = encrypt
35
38
  this.envKeysFilepath = envKeysFilepath
36
- this.opsOn = opsOn
39
+ this.noOps = noOps
37
40
  this.noCreate = noCreate
38
41
 
39
42
  this.processedEnvs = []
@@ -42,7 +45,7 @@ class Sets {
42
45
  this.readableFilepaths = new Set()
43
46
  }
44
47
 
45
- run () {
48
+ runSync () {
46
49
  // example
47
50
  // envs [
48
51
  // { type: 'envFile', value: '.env' }
@@ -50,7 +53,7 @@ class Sets {
50
53
 
51
54
  for (const env of this.envs) {
52
55
  if (env.type === TYPE_ENV_FILE) {
53
- this._setEnvFile(env.value)
56
+ this._setEnvFileSync(env.value)
54
57
  }
55
58
  }
56
59
 
@@ -61,7 +64,26 @@ class Sets {
61
64
  }
62
65
  }
63
66
 
64
- _setEnvFile (envFilepath) {
67
+ async run () {
68
+ // example
69
+ // envs [
70
+ // { type: 'envFile', value: '.env' }
71
+ // ]
72
+
73
+ for (const env of this.envs) {
74
+ if (env.type === TYPE_ENV_FILE) {
75
+ await this._setEnvFile(env.value)
76
+ }
77
+ }
78
+
79
+ return {
80
+ processedEnvs: this.processedEnvs,
81
+ changedFilepaths: [...this.changedFilepaths],
82
+ unchangedFilepaths: [...this.unchangedFilepaths]
83
+ }
84
+ }
85
+
86
+ _setEnvFileSync (envFilepath) {
65
87
  const row = {}
66
88
  row.key = this.key || null
67
89
  row.value = this.value || null
@@ -77,14 +99,119 @@ class Sets {
77
99
 
78
100
  if (!fsx.existsSync(filepath)) {
79
101
  if (this.noCreate) {
80
- detectEncoding(filepath) // throws ENOENT
102
+ detectEncodingSync(filepath) // throws ENOENT
103
+ } else {
104
+ fsx.writeFileXSync(filepath, '')
105
+ }
106
+ }
107
+
108
+ const encoding = detectEncodingSync(filepath)
109
+ let envSrc = fsx.readFileXSync(filepath, { encoding })
110
+
111
+ // blank files seeded by `set` should contain only the key being set
112
+ if (row.key && envSrc.trim().length === 0) {
113
+ envSrc = `${row.key}="${this.value}"\n`
114
+ seededWithInitialKey = true
115
+ }
116
+
117
+ const envParsed = dotenvParse(envSrc)
118
+ row.originalValue = envParsed[row.key] || null
119
+ if (seededWithInitialKey) {
120
+ row.originalValue = null
121
+ }
122
+ const wasPlainText = !isEncrypted(row.originalValue)
123
+ this.readableFilepaths.add(envFilepath)
124
+
125
+ if (this.encrypt) {
126
+ let publicKey
127
+ let privateKey
128
+
129
+ const { publicKeyName, privateKeyName } = keyNames(filepath)
130
+ const { publicKeyValue, privateKeyValue } = keyValuesSync(filepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
131
+
132
+ // first pass - provisionSync
133
+ if (!privateKeyValue && !publicKeyValue) {
134
+ const prov = provisionSync({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, noOps: this.noOps })
135
+ envSrc = prov.envSrc
136
+ publicKey = prov.publicKey
137
+ privateKey = prov.privateKey
138
+ row.privateKeyAdded = prov.privateKeyAdded
139
+ row.envKeysFilepath = prov.envKeysFilepath
140
+ } else if (privateKeyValue) {
141
+ const prov = provisionWithPrivateKey({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, privateKeyValue, publicKeyValue, publicKeyName })
142
+ publicKey = prov.publicKey
143
+ privateKey = prov.privateKey
144
+ envSrc = prov.envSrc
145
+
146
+ if (row.originalValue) {
147
+ row.originalValue = decryptKeyValue(row.key, row.originalValue, privateKeyName, privateKey)
148
+ }
149
+ } else if (publicKeyValue) {
150
+ publicKey = publicKeyValue
151
+ }
152
+
153
+ row.publicKey = publicKey
154
+ row.privateKey = privateKey
155
+ try {
156
+ row.encryptedValue = encryptValue(this.value, publicKey)
157
+ } catch {
158
+ throw new Errors({ publicKeyName, publicKey }).invalidPublicKey()
159
+ }
160
+ row.privateKeyName = privateKeyName
161
+ }
162
+
163
+ const goingFromPlainTextToEncrypted = wasPlainText && this.encrypt
164
+ const valueChanged = this.value !== row.originalValue
165
+ const shouldPersistSeededPlainValue = seededWithInitialKey && !this.encrypt
166
+
167
+ if (shouldPersistSeededPlainValue) {
168
+ row.envSrc = envSrc
169
+ this.changedFilepaths.add(envFilepath)
170
+ row.changed = true
171
+ } else if (goingFromPlainTextToEncrypted || valueChanged) {
172
+ row.envSrc = replace(envSrc, this.key, row.encryptedValue || this.value)
173
+ this.changedFilepaths.add(envFilepath)
174
+ row.changed = true
175
+ } else {
176
+ row.envSrc = envSrc
177
+ this.unchangedFilepaths.add(envFilepath)
178
+ row.changed = false
179
+ }
180
+ } catch (e) {
181
+ if (e.code === 'ENOENT') {
182
+ row.error = new Errors({ envFilepath, filepath }).missingEnvFile()
183
+ } else {
184
+ row.error = e
185
+ }
186
+ }
187
+
188
+ this.processedEnvs.push(row)
189
+ }
190
+
191
+ async _setEnvFile (envFilepath) {
192
+ const row = {}
193
+ row.key = this.key || null
194
+ row.value = this.value || null
195
+ row.type = TYPE_ENV_FILE
196
+
197
+ const filepath = path.resolve(envFilepath)
198
+ row.filepath = filepath
199
+ row.envFilepath = envFilepath
200
+ row.changed = false
201
+
202
+ try {
203
+ let seededWithInitialKey = false
204
+
205
+ if (!(await fsx.exists(filepath))) {
206
+ if (this.noCreate) {
207
+ await detectEncoding(filepath) // throws ENOENT
81
208
  } else {
82
- fsx.writeFileX(filepath, '')
209
+ await fsx.writeFileX(filepath, '')
83
210
  }
84
211
  }
85
212
 
86
- const encoding = detectEncoding(filepath)
87
- let envSrc = fsx.readFileX(filepath, { encoding })
213
+ const encoding = await detectEncoding(filepath)
214
+ let envSrc = await fsx.readFileX(filepath, { encoding })
88
215
 
89
216
  // blank files seeded by `set` should contain only the key being set
90
217
  if (row.key && envSrc.trim().length === 0) {
@@ -105,11 +232,11 @@ class Sets {
105
232
  let privateKey
106
233
 
107
234
  const { publicKeyName, privateKeyName } = keyNames(filepath)
108
- const { publicKeyValue, privateKeyValue } = keyValues(filepath, { keysFilepath: this.envKeysFilepath, opsOn: this.opsOn })
235
+ const { publicKeyValue, privateKeyValue } = await keyValues(filepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
109
236
 
110
237
  // first pass - provision
111
238
  if (!privateKeyValue && !publicKeyValue) {
112
- const prov = provision({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, opsOn: this.opsOn })
239
+ const prov = await provision({ envSrc, envFilepath, keysFilepath: this.envKeysFilepath, noOps: this.noOps })
113
240
  envSrc = prov.envSrc
114
241
  publicKey = prov.publicKey
115
242
  privateKey = prov.privateKey
@@ -20,8 +20,8 @@ const success = getColor('amber')
20
20
  const successv = (m) => getColor('amber')(`⟐ ${m}`)
21
21
  const info = getColor('gray')
22
22
  const help = getColor('dodgerblue')
23
- const verbose = getColor('plum')
24
- const debug = getColor('plum')
23
+ const verbose = (m) => getColor('plum')(`┆ ${m}`)
24
+ const debug = (m) => getColor('plum')(`┆ ${m}`)
25
25
 
26
26
  let currentLevel = levels.info // default log level
27
27
  let currentName = 'dotenvx' // default logger name
@@ -121,7 +121,7 @@ function setLogLevel (options) {
121
121
  logger.setLevel(logLevel)
122
122
  // Only log which level it's setting if it's not set to quiet mode
123
123
  if (!options.quiet || (options.quiet && logLevel !== 'error')) {
124
- logger.debug(`Setting log level to ${logLevel}`)
124
+ logger.debug(`setting log level to: ${logLevel}`)
125
125
  }
126
126
  }
127
127
 
@@ -1,5 +0,0 @@
1
- function sleep (ms) {
2
- return new Promise(resolve => setTimeout(resolve, ms))
3
- }
4
-
5
- module.exports = sleep