@dotenvx/dotenvx 1.59.1 → 1.60.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +18 -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 +22 -17
  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 +26 -14
  11. package/src/cli/actions/run.js +13 -6
  12. package/src/cli/actions/set.js +31 -17
  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 +15 -11
  25. package/src/lib/helpers/cryptography/provisionSync.js +51 -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 +20 -4
  40. package/src/lib/main.js +30 -22
  41. package/src/lib/services/decrypt.js +8 -8
  42. package/src/lib/services/encrypt.js +19 -12
  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 +26 -20
  49. package/src/lib/services/run.js +82 -9
  50. package/src/lib/services/sets.js +142 -13
  51. package/src/shared/logger.js +3 -3
  52. package/src/lib/helpers/sleep.js +0 -5
@@ -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
  }
@@ -7,15 +7,15 @@ const readProcessKey = require('./readProcessKey')
7
7
  const readFileKey = require('./readFileKey')
8
8
  const opsKeypair = require('../cryptography/opsKeypair')
9
9
 
10
- function invertForPrivateKeyName (filepath) {
10
+ async function invertForPrivateKeyName (filepath) {
11
11
  const PUBLIC_KEY_SCHEMA = 'DOTENV_PUBLIC_KEY'
12
12
  const PRIVATE_KEY_SCHEMA = 'DOTENV_PRIVATE_KEY'
13
13
 
14
- if (!fsx.existsSync(filepath)) {
14
+ if (!(await fsx.exists(filepath))) {
15
15
  return null
16
16
  }
17
17
 
18
- const envSrc = fsx.readFileX(filepath)
18
+ const envSrc = await fsx.readFileX(filepath)
19
19
  const envParsed = dotenvParse(envSrc)
20
20
 
21
21
  let publicKeyName
@@ -32,9 +32,9 @@ function invertForPrivateKeyName (filepath) {
32
32
  return null
33
33
  }
34
34
 
35
- function keyValues (filepath, opts = {}) {
35
+ async function keyValues (filepath, opts = {}) {
36
36
  let keysFilepath = opts.keysFilepath || null
37
- const opsOn = opts.opsOn === true
37
+ const noOps = opts.noOps === true
38
38
  const names = keyNames(filepath)
39
39
  const publicKeyName = names.publicKeyName // DOTENV_PUBLIC_KEY_${ENVIRONMENT}
40
40
  let privateKeyName = names.privateKeyName // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
@@ -45,7 +45,7 @@ function keyValues (filepath, opts = {}) {
45
45
  // public key: process.env first, then .env*
46
46
  publicKey = readProcessKey(publicKeyName)
47
47
  if (!publicKey) {
48
- publicKey = readFileKey(publicKeyName, filepath) || null
48
+ publicKey = await readFileKey(publicKeyName, filepath) || null
49
49
  }
50
50
 
51
51
  // private key: process.env first, then .env.keys, then invert public key
@@ -57,28 +57,28 @@ function keyValues (filepath, opts = {}) {
57
57
  keysFilepath = path.resolve(path.dirname(filepath), '.env.keys') // typical scenario
58
58
  }
59
59
 
60
- privateKey = readFileKey(privateKeyName, keysFilepath)
60
+ privateKey = await readFileKey(privateKeyName, keysFilepath)
61
61
  }
62
62
  // invert
63
63
  if (!privateKey) {
64
- privateKeyName = invertForPrivateKeyName(filepath)
64
+ privateKeyName = await invertForPrivateKeyName(filepath)
65
65
  if (privateKeyName) {
66
66
  privateKey = readProcessKey(privateKeyName)
67
67
  if (!privateKey) {
68
- privateKey = readFileKey(privateKeyName, keysFilepath)
68
+ privateKey = await readFileKey(privateKeyName, keysFilepath)
69
69
  }
70
70
  }
71
71
  }
72
72
 
73
73
  // ops
74
- if (opsOn && !privateKey && publicKey && publicKey.length > 0) {
75
- const kp = opsKeypair(publicKey)
74
+ if (!noOps && !privateKey && publicKey && publicKey.length > 0) {
75
+ const kp = await opsKeypair(publicKey)
76
76
  privateKey = kp.privateKey
77
77
  }
78
78
 
79
79
  return {
80
80
  publicKeyValue: publicKey || null, // important to make sure name is rendered
81
- privateKeyValue: privateKey || null // importan to make sure name is rendered
81
+ privateKeyValue: privateKey || null // important to make sure name is rendered
82
82
  }
83
83
  }
84
84
 
@@ -0,0 +1,85 @@
1
+ const path = require('path')
2
+
3
+ const fsx = require('./../fsx')
4
+ const dotenvParse = require('./../dotenvParse')
5
+ const keyNames = require('./keyNames')
6
+ const readProcessKey = require('./readProcessKey')
7
+ const readFileKeySync = require('./readFileKeySync')
8
+ const opsKeypairSync = require('../cryptography/opsKeypairSync')
9
+
10
+ function invertForPrivateKeyName (filepath) {
11
+ const PUBLIC_KEY_SCHEMA = 'DOTENV_PUBLIC_KEY'
12
+ const PRIVATE_KEY_SCHEMA = 'DOTENV_PRIVATE_KEY'
13
+
14
+ if (!fsx.existsSync(filepath)) {
15
+ return null
16
+ }
17
+
18
+ const envSrc = fsx.readFileXSync(filepath)
19
+ const envParsed = dotenvParse(envSrc)
20
+
21
+ let publicKeyName
22
+ for (const keyName of Object.keys(envParsed)) {
23
+ if (keyName === PUBLIC_KEY_SCHEMA || keyName.startsWith(PUBLIC_KEY_SCHEMA)) {
24
+ publicKeyName = keyName // find DOTENV_PUBLIC_KEY* in filename
25
+ }
26
+ }
27
+
28
+ if (publicKeyName) {
29
+ return publicKeyName.replace(PUBLIC_KEY_SCHEMA, PRIVATE_KEY_SCHEMA) // return inverted (DOTENV_PUBLIC_KEY* -> DOTENV_PRIVATE_KEY*) if found
30
+ }
31
+
32
+ return null
33
+ }
34
+
35
+ function keyValuesSync (filepath, opts = {}) {
36
+ let keysFilepath = opts.keysFilepath || null
37
+ const noOps = opts.noOps === true
38
+ const names = keyNames(filepath)
39
+ const publicKeyName = names.publicKeyName // DOTENV_PUBLIC_KEY_${ENVIRONMENT}
40
+ let privateKeyName = names.privateKeyName // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
41
+
42
+ let publicKey = null
43
+ let privateKey = null
44
+
45
+ // public key: process.env first, then .env*
46
+ publicKey = readProcessKey(publicKeyName)
47
+ if (!publicKey) {
48
+ publicKey = readFileKeySync(publicKeyName, filepath) || null
49
+ }
50
+
51
+ // private key: process.env first, then .env.keys, then invert public key
52
+ privateKey = readProcessKey(privateKeyName)
53
+ if (!privateKey) {
54
+ if (keysFilepath) { // user specified -fk flag
55
+ keysFilepath = path.resolve(keysFilepath)
56
+ } else {
57
+ keysFilepath = path.resolve(path.dirname(filepath), '.env.keys') // typical scenario
58
+ }
59
+
60
+ privateKey = readFileKeySync(privateKeyName, keysFilepath)
61
+ }
62
+ // invert
63
+ if (!privateKey) {
64
+ privateKeyName = invertForPrivateKeyName(filepath)
65
+ if (privateKeyName) {
66
+ privateKey = readProcessKey(privateKeyName)
67
+ if (!privateKey) {
68
+ privateKey = readFileKeySync(privateKeyName, keysFilepath)
69
+ }
70
+ }
71
+ }
72
+
73
+ // ops
74
+ if (!noOps && !privateKey && publicKey && publicKey.length > 0) {
75
+ const kp = opsKeypairSync(publicKey)
76
+ privateKey = kp.privateKey
77
+ }
78
+
79
+ return {
80
+ publicKeyValue: publicKey || null, // important to make sure name is rendered
81
+ privateKeyValue: privateKey || null // important to make sure name is rendered
82
+ }
83
+ }
84
+
85
+ module.exports = keyValuesSync
@@ -1,14 +1,16 @@
1
1
  const fsx = require('./../fsx')
2
2
  const dotenvParse = require('./../dotenvParse')
3
3
 
4
- function readFileKey (keyName, filepath) {
5
- if (fsx.existsSync(filepath)) {
6
- const src = fsx.readFileX(filepath)
7
- const parsed = dotenvParse(src)
8
-
9
- if (parsed[keyName] && parsed[keyName].length > 0) {
10
- return parsed[keyName]
11
- }
4
+ async function readFileKey (keyName, filepath) {
5
+ if (!(await fsx.exists(filepath))) {
6
+ return undefined
7
+ }
8
+
9
+ const src = await fsx.readFileX(filepath)
10
+ const parsed = dotenvParse(src)
11
+
12
+ if (parsed[keyName] && parsed[keyName].length > 0) {
13
+ return parsed[keyName]
12
14
  }
13
15
  }
14
16
 
@@ -0,0 +1,15 @@
1
+ const fsx = require('./../fsx')
2
+ const dotenvParse = require('./../dotenvParse')
3
+
4
+ function readFileKeySync (keyName, filepath) {
5
+ if (fsx.existsSync(filepath)) {
6
+ const src = fsx.readFileXSync(filepath)
7
+ const parsed = dotenvParse(src)
8
+
9
+ if (parsed[keyName] && parsed[keyName].length > 0) {
10
+ return parsed[keyName]
11
+ }
12
+ }
13
+ }
14
+
15
+ module.exports = readFileKeySync
@@ -1,35 +1,23 @@
1
1
  const SAMPLE_ENV_KIT = `
2
2
  HELLO="Dotenvx"
3
3
 
4
- # ── Database ─────────────────────────────────────
4
+ # ── Hosting ──────────────────────────────────────
5
+ AWS_ACCESS_KEY_ID="xxxx"
6
+ AWS_SECRET_ACCESS_KEY="xxxx"
5
7
  DATABASE_URL="postgresql://postgres:pass@db.ref.supabase.co:5432/postgres"
8
+ VERCEL_TOKEN="vcp_xxxx"
6
9
 
7
- # ── Auth ─────────────────────────────────────────
8
- AUTH0_CLIENT_ID="xxxx"
9
- AUTH0_CLIENT_SECRET="xxxx"
10
-
11
- # ── AI / LLM ────────────────────────────────────
10
+ # ── AI ───────────────────────────────────────────
12
11
  OPENAI_API_KEY="sk-xxxx"
13
12
  ANTHROPIC_API_KEY="sk-ant-xxxx"
14
13
 
15
- # ── Email ────────────────────────────────────────
14
+ # ── Infrastructure ───────────────────────────────
15
+ AUTH0_CLIENT_ID="xxxx"
16
+ AUTH0_CLIENT_SECRET="xxxx"
16
17
  RESEND_API_KEY="re_xxxx"
17
-
18
- # ── Cloud Storage ────────────────────────────────
19
- AWS_ACCESS_KEY_ID="xxxx"
20
- AWS_SECRET_ACCESS_KEY="xxxx"
21
-
22
- # ── Analytics / Monitoring ───────────────────────
23
- SENTRY_DSN="https://hex@o1234.ingest.us.sentry.io/1234567"
24
-
25
- # ── Payments ─────────────────────────────────────
26
18
  STRIPE_API_KEY="sk_test_xxxx"
27
-
28
- # ── Feature Flags ────────────────────────────────
29
19
  FLAGSMITH_ENV_ID="xxxx"
30
-
31
- # ── CI/CD / Deployment ──────────────────────────
32
- VERCEL_TOKEN="vcp_xxxx"
20
+ SENTRY_DSN="https://hex@o1234.ingest.us.sentry.io/1234567"
33
21
  `.trimStart()
34
22
 
35
23
  module.exports = SAMPLE_ENV_KIT
package/src/lib/main.d.ts CHANGED
@@ -151,7 +151,12 @@ export interface DotenvConfigOptions {
151
151
  * Turn off Dotenvx Ops features - https://dotenvx.com/ops
152
152
  *
153
153
  * @default false
154
- * @example require('@dotenvx/dotenvx').config({ opsOff: true })
154
+ * @example require('@dotenvx/dotenvx').config({ noOps: true })
155
+ */
156
+ noOps?: boolean;
157
+
158
+ /**
159
+ * @deprecated use `noOps` instead.
155
160
  */
156
161
  opsOff?: boolean;
157
162
  }
@@ -210,7 +215,12 @@ export interface SetOptions {
210
215
  * Turn off Dotenvx Ops features - https://dotenvx.com/ops
211
216
  *
212
217
  * @default false
213
- * @example require('@dotenvx/dotenvx').set(key, value, { opsOff: true })
218
+ * @example require('@dotenvx/dotenvx').set(key, value, { noOps: true })
219
+ */
220
+ noOps?: boolean;
221
+
222
+ /**
223
+ * @deprecated use `noOps` instead.
214
224
  */
215
225
  opsOff?: boolean;
216
226
  }
@@ -225,7 +235,8 @@ export type SetProcessedEnv = {
225
235
  encryptedValue?: string;
226
236
  publicKey?: string;
227
237
  privateKey?: string;
228
- privateKeyAdded?: boolean;
238
+ localPrivateKeyAdded?: boolean;
239
+ remotePrivateKeyAdded?: boolean;
229
240
  privateKeyName?: string;
230
241
  error?: Error;
231
242
  };
@@ -285,7 +296,12 @@ export interface GetOptions {
285
296
  * Turn off Dotenvx Ops features - https://dotenvx.com/ops
286
297
  *
287
298
  * @default false
288
- * @example require('@dotenvx/dotenvx').get('KEY', { opsOff: true })
299
+ * @example require('@dotenvx/dotenvx').get('KEY', { noOps: true })
300
+ */
301
+ noOps?: boolean;
302
+
303
+ /**
304
+ * @deprecated use `noOps` instead.
289
305
  */
290
306
  opsOff?: boolean;
291
307
  }
package/src/lib/main.js CHANGED
@@ -12,6 +12,7 @@ const Sets = require('./services/sets')
12
12
  const Get = require('./services/get')
13
13
  const Keypair = require('./services/keypair')
14
14
  const Genexample = require('./services/genexample')
15
+ const Session = require('./../db/session')
15
16
 
16
17
  // helpers
17
18
  const buildEnvs = require('./helpers/buildEnvs')
@@ -39,27 +40,22 @@ const config = function (options = {}) {
39
40
  // envKeysFile
40
41
  const envKeysFile = options.envKeysFile
41
42
 
42
- // dotenvx-ops related
43
- const opsOn = options.opsOff !== true
44
-
45
43
  if (options) {
46
44
  setLogLevel(options)
47
45
  setLogName(options)
48
46
  setLogVersion(options)
49
47
  }
50
48
 
49
+ // dotenvx-ops related
50
+ const noOps = resolveNoOps(options)
51
+
51
52
  try {
52
53
  const envs = buildEnvs(options)
53
54
  const {
54
55
  processedEnvs,
55
56
  readableFilepaths,
56
57
  uniqueInjectedKeys
57
- } = new Run(envs, overload, processEnv, envKeysFile, opsOn).run()
58
-
59
- if (opsOn) {
60
- // removed radar feature for now. contact me at mot@dotenvx.com if still needed for your organization.
61
- // try { new Ops().observe({ beforeEnv, processedEnvs, afterEnv }) } catch {}
62
- }
58
+ } = new Run(envs, overload, processEnv, envKeysFile, noOps).runSync()
63
59
 
64
60
  let lastError
65
61
  /** @type {Record<string, string>} */
@@ -169,13 +165,13 @@ const set = function (key, value, options = {}) {
169
165
 
170
166
  const envs = buildEnvs(options)
171
167
  const envKeysFilepath = options.envKeysFile
172
- const opsOn = options.opsOff !== true
168
+ const noOps = resolveNoOps(options)
173
169
 
174
170
  const {
175
171
  processedEnvs,
176
172
  changedFilepaths,
177
173
  unchangedFilepaths
178
- } = new Sets(key, value, envs, encrypt, envKeysFilepath, opsOn).run()
174
+ } = new Sets(key, value, envs, encrypt, envKeysFilepath, noOps).runSync()
179
175
 
180
176
  let withEncryption = ''
181
177
 
@@ -191,15 +187,23 @@ const set = function (key, value, options = {}) {
191
187
  const message = error.messageWithHelp || (error.help ? `${error.message}. ${error.help}` : error.message)
192
188
  logger.warn(message)
193
189
  } else {
194
- fsx.writeFileX(processedEnv.filepath, processedEnv.envSrc)
190
+ fsx.writeFileXSync(processedEnv.filepath, processedEnv.envSrc)
195
191
 
196
192
  logger.verbose(`${processedEnv.key} set${withEncryption} (${processedEnv.envFilepath})`)
197
193
  logger.debug(`${processedEnv.key} set${withEncryption} to ${processedEnv.value} (${processedEnv.envFilepath})`)
198
194
  }
199
195
  }
200
196
 
201
- const keyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.privateKeyAdded)
202
- const keyAddedSuffix = keyAddedEnv ? ` + key (${localDisplayPath(keyAddedEnv.envKeysFilepath)})` : ''
197
+ let keyAddedSuffix = ''
198
+ const localKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.localPrivateKeyAdded)
199
+ const remoteKeyAddedEnv = processedEnvs.find((processedEnv) => processedEnv.remotePrivateKeyAdded)
200
+
201
+ if (localKeyAddedEnv) {
202
+ keyAddedSuffix = ` + key (${localDisplayPath(localKeyAddedEnv.envKeysFilepath)})`
203
+ }
204
+ if (remoteKeyAddedEnv) {
205
+ keyAddedSuffix = ' + key ⛨'
206
+ }
203
207
 
204
208
  if (changedFilepaths.length > 0) {
205
209
  if (encrypt) {
@@ -207,11 +211,11 @@ const set = function (key, value, options = {}) {
207
211
  } else {
208
212
  logger.success(`◇ set ${key} (${changedFilepaths.join(',')})`)
209
213
  }
210
- } else if (encrypt && keyAddedEnv) {
211
- const keyAddedEnvFilepath = keyAddedEnv.envFilepath || changedFilepaths[0] || '.env'
214
+ } else if (encrypt && localKeyAddedEnv) {
215
+ const keyAddedEnvFilepath = localKeyAddedEnv.envFilepath || changedFilepaths[0] || '.env'
212
216
  logger.success(`◈ encrypted ${key} (${keyAddedEnvFilepath})${keyAddedSuffix}`)
213
217
  } else if (unchangedFilepaths.length > 0) {
214
- logger.info(`○ no changes (${unchangedFilepaths})`)
218
+ logger.info(`○ no change (${unchangedFilepaths})`)
215
219
  } else {
216
220
  // do nothing
217
221
  }
@@ -228,12 +232,12 @@ const set = function (key, value, options = {}) {
228
232
  /* @type {import('./main').get} */
229
233
  const get = function (key, options = {}) {
230
234
  const envs = buildEnvs(options)
231
- const opsOn = options.opsOff !== true
235
+ const noOps = resolveNoOps(options)
232
236
 
233
237
  // ignore
234
238
  const ignore = options.ignore || []
235
239
 
236
- const { parsed, errors } = new Get(key, envs, options.overload, options.all, options.envKeysFile, opsOn).run()
240
+ const { parsed, errors } = new Get(key, envs, options.overload, options.all, options.envKeysFile, noOps).runSync()
237
241
 
238
242
  for (const error of errors || []) {
239
243
  if (ignore.includes(error.code)) {
@@ -286,9 +290,8 @@ const genexample = function (directory, envFile) {
286
290
  }
287
291
 
288
292
  /** @type {import('./main').keypair} */
289
- const keypair = function (envFile, key, envKeysFile = null, opsOff = false) {
290
- const opsOn = opsOff !== true
291
- const keypairs = new Keypair(envFile, envKeysFile, opsOn).run()
293
+ const keypair = function (envFile, key, envKeysFile = null, noOps = false) {
294
+ const keypairs = new Keypair(envFile, envKeysFile, noOps).runSync()
292
295
  if (key) {
293
296
  return keypairs[key]
294
297
  } else {
@@ -296,6 +299,11 @@ const keypair = function (envFile, key, envKeysFile = null, opsOff = false) {
296
299
  }
297
300
  }
298
301
 
302
+ function resolveNoOps (options = {}) {
303
+ const sesh = new Session()
304
+ return options.noOps === true || options.opsOff === true || sesh.noOpsSync()
305
+ }
306
+
299
307
  module.exports = {
300
308
  // dotenv proxies
301
309
  config,
@@ -25,19 +25,19 @@ const dotenvParse = require('./../helpers/dotenvParse')
25
25
  const detectEncoding = require('./../helpers/detectEncoding')
26
26
 
27
27
  class Decrypt {
28
- constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, opsOn = false) {
28
+ constructor (envs = [], key = [], excludeKey = [], envKeysFilepath = null, noOps = false) {
29
29
  this.envs = determine(envs, process.env)
30
30
  this.key = key
31
31
  this.excludeKey = excludeKey
32
32
  this.envKeysFilepath = envKeysFilepath
33
- this.opsOn = opsOn
33
+ this.noOps = noOps
34
34
 
35
35
  this.processedEnvs = []
36
36
  this.changedFilepaths = new Set()
37
37
  this.unchangedFilepaths = new Set()
38
38
  }
39
39
 
40
- run () {
40
+ async run () {
41
41
  // example
42
42
  // envs [
43
43
  // { type: 'envFile', value: '.env' }
@@ -51,7 +51,7 @@ class Decrypt {
51
51
 
52
52
  for (const env of this.envs) {
53
53
  if (env.type === TYPE_ENV_FILE) {
54
- this._decryptEnvFile(env.value)
54
+ await this._decryptEnvFile(env.value)
55
55
  }
56
56
  }
57
57
 
@@ -62,7 +62,7 @@ class Decrypt {
62
62
  }
63
63
  }
64
64
 
65
- _decryptEnvFile (envFilepath) {
65
+ async _decryptEnvFile (envFilepath) {
66
66
  const row = {}
67
67
  row.keys = []
68
68
  row.type = TYPE_ENV_FILE
@@ -72,12 +72,12 @@ class Decrypt {
72
72
  row.envFilepath = envFilepath
73
73
 
74
74
  try {
75
- const encoding = detectEncoding(filepath)
76
- let envSrc = fsx.readFileX(filepath, { encoding })
75
+ const encoding = await detectEncoding(filepath)
76
+ let envSrc = await fsx.readFileX(filepath, { encoding })
77
77
  const envParsed = dotenvParse(envSrc)
78
78
 
79
79
  const { privateKeyName } = keyNames(envFilepath)
80
- const { privateKeyValue } = keyValues(envFilepath, { keysFilepath: this.envKeysFilepath, opsOn: this.opsOn })
80
+ const { privateKeyValue } = await keyValues(envFilepath, { keysFilepath: this.envKeysFilepath, noOps: this.noOps })
81
81
 
82
82
  row.privateKey = privateKeyValue
83
83
  row.privateKeyName = privateKeyName