@dotenvx/dotenvx 2.20.1 → 2.22.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/CHANGELOG.md CHANGED
@@ -2,7 +2,28 @@
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/v2.20.1...main)
5
+ [Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.22.0...main)
6
+
7
+ ## [2.22.0](https://github.com/dotenvx/dotenvx/compare/v2.21.0...v2.22.0) (2026-08-27)
8
+
9
+ ### Added
10
+
11
+ * Install verified binaries directly from GitHub Releases ([#956](https://github.com/dotenvx/dotenvx/pull/956))
12
+
13
+ ### Changed
14
+
15
+ * Fall back to `~/.local/bin` when `/usr/local/bin` is not writable ([#955](https://github.com/dotenvx/dotenvx/pull/955))
16
+
17
+ ## [2.21.0](https://github.com/dotenvx/dotenvx/compare/v2.20.1...v2.21.0) (2026-08-10)
18
+
19
+ ### Added
20
+
21
+ * Add `dotenvx enc` and `dotenvx dec` shorthands for encrypt and decrypt ([#943](https://github.com/dotenvx/dotenvx/pull/943))
22
+ * Add convenient `dx` alias for shorter `dx run --`, `dx enc`, etc ([#944](https://github.com/dotenvx/dotenvx/pull/944))
23
+
24
+ ### Changed
25
+
26
+ * Patch typescript types for `path` ([#942](https://github.com/dotenvx/dotenvx/pull/942))
6
27
 
7
28
  ## [2.20.1](https://github.com/dotenvx/dotenvx/compare/v2.20.0...v2.20.1) (2026-08-07)
8
29
 
package/README.md CHANGED
@@ -122,7 +122,7 @@ Hello World # with dotenvx
122
122
  > :-D
123
123
  ```
124
124
 
125
- see [quickstart guides](https://dotenvx.com/docs/quickstart)
125
+ see [quickstart guides](https://dotenvx.com/docs/quickstarts)
126
126
 
127
127
  More examples
128
128
 
@@ -186,7 +186,7 @@ $ dotenvx run --redact -- agent -p --force 'Run `dotenvx get HELLO` and echo bac
186
186
  Hello [REDACTED]
187
187
  ```
188
188
 
189
- see [Cursor redaction guide](https://dotenvx.com/docs/secrets-in-cursor)
189
+ see [Cursor redaction guide](https://dotenvx.com/docs/cursor)
190
190
 
191
191
  </details>
192
192
  <details><summary>1Password 🔐</summary><br>
@@ -199,7 +199,7 @@ $ dotenvx run -- sh -c 'echo Hello $HELLO'
199
199
  Hello World
200
200
  ```
201
201
 
202
- see [1Password guide](https://dotenvx.com/docs/secrets-in-1password)
202
+ see [1Password guide](https://dotenvx.com/docs/1password)
203
203
 
204
204
  </details>
205
205
  <details><summary>Bitwarden 🛡️</summary><br>
@@ -272,7 +272,7 @@ export async function GET() {
272
272
  }
273
273
  ```
274
274
 
275
- see [astro guide](https://dotenvx.com/docs/secrets-in-astro)
275
+ see [astro guide](https://dotenvx.com/docs/astro)
276
276
 
277
277
  </details>
278
278
  <details><summary>Expo 🧭</summary><br>
@@ -292,7 +292,7 @@ Preface Expo scripts with `dotenvx run --`.
292
292
  }
293
293
  ```
294
294
 
295
- see [expo guide](https://dotenvx.com/docs/secrets-in-expo)
295
+ see [expo guide](https://dotenvx.com/docs/expo)
296
296
 
297
297
  </details>
298
298
  <details><summary>Next.js ▲</summary><br>
@@ -413,7 +413,7 @@ $ dotenvx run -- python3 index.py
413
413
  Hello World
414
414
  ```
415
415
 
416
- see [extended python guide](https://dotenvx.com/docs/quickstart)
416
+ see [extended python guide](https://dotenvx.com/docs/python)
417
417
 
418
418
  </details>
419
419
  <details><summary>PHP 🐘</summary><br>
@@ -426,7 +426,7 @@ $ dotenvx run -- php index.php
426
426
  Hello World
427
427
  ```
428
428
 
429
- see [extended php guide](https://dotenvx.com/docs/quickstart)
429
+ see [extended php guide](https://dotenvx.com/docs/php)
430
430
 
431
431
  </details>
432
432
  <details><summary>Ruby 💎</summary><br>
@@ -439,7 +439,7 @@ $ dotenvx run -- ruby index.rb
439
439
  Hello World
440
440
  ```
441
441
 
442
- see [extended ruby guide](https://dotenvx.com/docs/quickstart)
442
+ see [extended ruby guide](https://dotenvx.com/docs/ruby)
443
443
 
444
444
  </details>
445
445
  <details><summary>Go 🐹</summary><br>
@@ -452,7 +452,7 @@ $ dotenvx run -- go run main.go
452
452
  Hello World
453
453
  ```
454
454
 
455
- see [extended go guide](https://dotenvx.com/docs/quickstart)
455
+ see [extended go guide](https://dotenvx.com/docs/go)
456
456
 
457
457
  </details>
458
458
  <details><summary>Rust 🦀</summary><br>
@@ -465,7 +465,7 @@ $ dotenvx run -- cargo run
465
465
  Hello World
466
466
  ```
467
467
 
468
- see [extended rust guide](https://dotenvx.com/docs/quickstart)
468
+ see [extended rust guide](https://dotenvx.com/docs/rust)
469
469
 
470
470
  </details>
471
471
  <details><summary>Java ☕️</summary><br>
@@ -552,7 +552,7 @@ $ dotenvx run -- bin/rails s
552
552
  $ dotenvx run -- php artisan serve
553
553
  ```
554
554
 
555
- see [framework guides](https://dotenvx.com/docs#frameworks)
555
+ see [framework guides](https://dotenvx.com/docs/quickstarts)
556
556
 
557
557
  </details>
558
558
  <details><summary>Docker 🐳</summary><br>
@@ -570,7 +570,7 @@ RUN curl -fsS https://dotenvx.sh/install.sh | sh
570
570
  CMD ["/usr/local/bin/dotenvx", "run", "--", "echo", "Hello $HELLO"]
571
571
  ```
572
572
 
573
- see [docker guide](https://dotenvx.com/docs/platforms/docker)
573
+ see [docker guide](https://dotenvx.com/docs/docker)
574
574
 
575
575
  </details>
576
576
  <details><summary>CI/CDs 🐙</summary><br>
@@ -592,7 +592,7 @@ jobs:
592
592
  DOTENV_KEY: ${{ secrets.DOTENV_KEY }}
593
593
  ```
594
594
 
595
- see [github actions guide](https://dotenvx.com/docs/cis/github-actions)
595
+ see [github actions guide](https://dotenvx.com/docs/github-actions)
596
596
 
597
597
  </details>
598
598
  <details><summary>Platforms</summary><br>
@@ -608,7 +608,7 @@ RUN curl -fsS https://dotenvx.sh | sh
608
608
  npm install @dotenvx/dotenvx --save
609
609
  ```
610
610
 
611
- see [platform guides](https://dotenvx.com/docs#platforms)
611
+ see [platform guides](https://dotenvx.com/docs/platforms)
612
612
 
613
613
  </details>
614
614
  <details><summary>Process Managers</summary><br>
@@ -620,7 +620,7 @@ see [platform guides](https://dotenvx.com/docs#platforms)
620
620
  },
621
621
  ```
622
622
 
623
- see [process manager guides](https://dotenvx.com/docs#process-managers)
623
+ see [process manager guides](https://dotenvx.com/docs/process-managers/pm2)
624
624
 
625
625
  </details>
626
626
  <details><summary>npx</summary><br>
@@ -2395,7 +2395,7 @@ $ echo "HELLO=World" > .env
2395
2395
  $ dotenvx encrypt --stdout
2396
2396
  #/-------------------[DOTENV_PUBLIC_KEY]--------------------/
2397
2397
  #/ public-key encryption for .env files /
2398
- #/ [how it works](https://dotenvx.com/encryption) /
2398
+ #/ [how it works](https://dotenvx.com/docs/quickstart/encryption) /
2399
2399
  #/----------------------------------------------------------/
2400
2400
  DOTENV_PUBLIC_KEY="034af93e93708b994c10f236c96ef88e47291066946cce2e8d98c9e02c741ced45"
2401
2401
  # .env
@@ -2527,7 +2527,7 @@ Decrypt the contents of an encrypted `.env` file and send to stdout.
2527
2527
  $ dotenvx decrypt --stdout
2528
2528
  #/-------------------[DOTENV_PUBLIC_KEY]--------------------/
2529
2529
  #/ public-key encryption for .env files /
2530
- #/ [how it works](https://dotenvx.com/encryption) /
2530
+ #/ [how it works](https://dotenvx.com/docs/quickstart/encryption) /
2531
2531
  #/----------------------------------------------------------/
2532
2532
  DOTENV_PUBLIC_KEY="034af93e93708b994c10f236c96ef88e47291066946cce2e8d98c9e02c741ced45"
2533
2533
  # .env
@@ -3804,22 +3804,22 @@ WRONG_PRIVATE_KEY= # the supplied private key cannot decrypt the value
3804
3804
  > Encrypt and run secrets in your language, framework, or tool.
3805
3805
  >
3806
3806
 
3807
- * [Claude](https://dotenvx.com/docs/secrets-in-claude)
3808
- * [Codex](https://dotenvx.com/docs/secrets-in-codex)
3809
- * [1Password](https://dotenvx.com/docs/secrets-in-1password)
3810
- * [Bitwarden](https://dotenvx.com/docs/secrets-in-bitwarden)
3811
- * [Node.js](https://dotenvx.com/docs/secrets-in-nodejs)
3812
- * [Next.js](https://dotenvx.com/docs/secrets-in-nextjs)
3813
- * [Express](https://dotenvx.com/docs/secrets-in-express)
3814
- * [Astro](https://dotenvx.com/docs/secrets-in-astro)
3815
- * [Expo](https://dotenvx.com/docs/secrets-in-expo)
3816
- * [Python](https://dotenvx.com/docs/secrets-in-python)
3817
- * [Ruby](https://dotenvx.com/docs/secrets-in-ruby)
3818
- * [Go](https://dotenvx.com/docs/secrets-in-go)
3819
- * [PHP](https://dotenvx.com/docs/secrets-in-php)
3820
- * [Rust](https://dotenvx.com/docs/secrets-in-rust)
3821
-
3822
- [Browse all quickstarts](https://dotenvx.com/docs)
3807
+ * [Claude](https://dotenvx.com/docs/claude)
3808
+ * [Codex](https://dotenvx.com/docs/codex)
3809
+ * [1Password](https://dotenvx.com/docs/1password)
3810
+ * [Bitwarden](https://dotenvx.com/docs/bitwarden)
3811
+ * [Node.js](https://dotenvx.com/docs/nodejs)
3812
+ * [Next.js](https://dotenvx.com/docs/nextjs)
3813
+ * [Express](https://dotenvx.com/docs/express)
3814
+ * [Astro](https://dotenvx.com/docs/astro)
3815
+ * [Expo](https://dotenvx.com/docs/expo)
3816
+ * [Python](https://dotenvx.com/docs/python)
3817
+ * [Ruby](https://dotenvx.com/docs/ruby)
3818
+ * [Go](https://dotenvx.com/docs/go)
3819
+ * [PHP](https://dotenvx.com/docs/php)
3820
+ * [Rust](https://dotenvx.com/docs/rust)
3821
+
3822
+ [Browse all quickstarts](https://dotenvx.com/docs/quickstarts)
3823
3823
 
3824
3824
  &nbsp;
3825
3825
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.20.1",
2
+ "version": "2.22.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a secure dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -39,7 +39,8 @@
39
39
  "./package.json": "./package.json"
40
40
  },
41
41
  "bin": {
42
- "dotenvx": "./src/cli/dotenvx.js"
42
+ "dotenvx": "./src/cli/dotenvx.js",
43
+ "dx": "./src/cli/dotenvx.js"
43
44
  },
44
45
  "scripts": {
45
46
  "standard": "standard",
@@ -142,37 +142,45 @@ program.command('del')
142
142
  })
143
143
 
144
144
  // dotenvx encrypt
145
- program.command('encrypt')
146
- .description('encrypt .env file(s)')
147
- .option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
148
- .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
149
- .option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
150
- .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
151
- .option('--stdout', 'send to stdout')
152
- .option('--token <token>', 'set Armor ⛨ token')
153
- .option('--no-create', 'do not create .env file(s) when missing')
154
- .option('--no-armor', 'disable Dotenvx Armor features')
155
- .option('--no-native', 'disable OS secret store features')
156
- .action(function (...args) {
157
- this.envs = envs
158
- return require('./actions/encrypt').apply(this, args)
159
- })
145
+ function configureEncryptCommand (command) {
146
+ return command
147
+ .description('encrypt .env file(s)')
148
+ .option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
149
+ .option('-fk, --env-keys-file <path>', 'path to your .env.keys file (default: same path as your env file)')
150
+ .option('-k, --key <keys...>', 'keys(s) to encrypt (default: all keys in file)')
151
+ .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from encryption (default: none)')
152
+ .option('--stdout', 'send to stdout')
153
+ .option('--token <token>', 'set Armor token')
154
+ .option('--no-create', 'do not create .env file(s) when missing')
155
+ .option('--no-armor', 'disable Dotenvx Armor features')
156
+ .option('--no-native', 'disable OS secret store features')
157
+ .action(function (...args) {
158
+ this.envs = envs
159
+ return require('./actions/encrypt').apply(this, args)
160
+ })
161
+ }
162
+ configureEncryptCommand(program.command('encrypt'))
163
+ configureEncryptCommand(program.command('enc', { hidden: true })) // shorthand
160
164
 
161
165
  // dotenvx decrypt
162
- program.command('decrypt')
163
- .description('decrypt .env file(s)')
164
- .option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
165
- .option('-fk, --env-keys-file <path>', 'path(s) to your .env.keys file(s) (default: same path as your env file)', collectEnvKeys)
166
- .option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
167
- .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
168
- .option('--no-armor', 'disable Dotenvx Armor features')
169
- .option('--no-native', 'disable OS secret store features')
170
- .option('--stdout', 'send to stdout')
171
- .option('--mask [characters]', 'mask stdout values, optionally setting visible characters')
172
- .action(function (...args) {
173
- this.envs = envs
174
- return require('./actions/decrypt').apply(this, args)
175
- })
166
+ function configureDecryptCommand (command) {
167
+ return command
168
+ .description('decrypt .env file(s)')
169
+ .option('-f, --env-file <path>', 'path(s) to your env file(s)', collectEnvs('envFile'), [])
170
+ .option('-fk, --env-keys-file <path>', 'path(s) to your .env.keys file(s) (default: same path as your env file)', collectEnvKeys)
171
+ .option('-k, --key <keys...>', 'keys(s) to decrypt (default: all keys in file)')
172
+ .option('-ek, --exclude-key <excludeKeys...>', 'keys(s) to exclude from decryption (default: none)')
173
+ .option('--no-armor', 'disable Dotenvx Armor features')
174
+ .option('--no-native', 'disable OS secret store features')
175
+ .option('--stdout', 'send to stdout')
176
+ .option('--mask [characters]', 'mask stdout values, optionally setting visible characters')
177
+ .action(function (...args) {
178
+ this.envs = envs
179
+ return require('./actions/decrypt').apply(this, args)
180
+ })
181
+ }
182
+ configureDecryptCommand(program.command('decrypt'))
183
+ configureDecryptCommand(program.command('dec', { hidden: true })) // shorthand
176
184
 
177
185
  // dotenvx keypair
178
186
  program.command('keypair')
@@ -323,11 +331,13 @@ program.command('help [command]')
323
331
  }
324
332
  })
325
333
 
326
- // dotenvx armor
334
+ // security sections (hidden commands advertised here)
327
335
  program.addHelpText('after', ' ')
328
- program.addHelpText('after', 'Professional Security: ')
336
+ program.addHelpText('after', 'Better Security:')
329
337
  program.addHelpText('after', ' lock ⊡ lock private keys with a local passphrase')
330
338
  program.addHelpText('after', ' native ⌥ move private keys into your OS secret store')
339
+ program.addHelpText('after', ' ')
340
+ program.addHelpText('after', 'For Security Teams:')
331
341
  program.addHelpText('after', ' armor ⛨ move private keys into Dotenvx Armor [www.dotenvx.com/armor]')
332
342
  program.addHelpText('after', ' curl ⛨ call authenticated api Dotenvx Armor [www.dotenvx.com/armor]')
333
343
 
package/src/cli/help.js CHANGED
@@ -108,30 +108,62 @@ Endpoints:
108
108
  GET /api/account
109
109
  GET /api/armor/keypairs
110
110
  GET /api/armor/keypairs/:public_key
111
+ POST /api/armor/keypairs/:public_key/name
112
+ POST /api/armor/keypairs/:public_key/members/:member_id/grant
113
+ POST /api/armor/keypairs/:public_key/members/:member_id/revoke
111
114
  POST /api/armor/keypairs/:public_key/settings/guard
112
115
  POST /api/armor/keypairs/:public_key/settings/enclave
113
116
  GET /api/teams
114
117
  GET /api/teams/:team
115
118
  GET /api/teams/:team/members
119
+ POST /api/teams/:team/members/:member_id/keypairs/:public_key/grant
120
+ POST /api/teams/:team/members/:member_id/keypairs/:public_key/revoke
116
121
  GET /api/teams/:team/invitations
117
122
  POST /api/teams/:team/invitations
118
- DELETE /api/teams/:team/invitations/:id
123
+ POST /api/teams/:team/invitations/:id/cancel
124
+ GET /api/join_requests
125
+ POST /api/join_requests
126
+ POST /api/join_requests/:id/cancel
127
+ GET /api/teams/:team/join_requests
128
+ POST /api/teams/:team/join_requests/:id/accept
129
+ POST /api/teams/:team/join_requests/:id/decline
130
+ POST /api/armor/:team/settings/join_requests
119
131
  GET /api/logs?team=:team
120
132
 
121
133
  Examples:
122
134
 
123
135
  dotenvx curl "https://armor.dotenvx.com/api/account"
124
136
  dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs"
137
+ dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs?page=1&per=100"
138
+ dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs?sort=name_asc"
139
+ dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs?sort=public_key_desc"
125
140
  dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs/PUBLIC_KEY"
141
+ dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs/PUBLIC_KEY/name" --data '{"name":"Production"}'
142
+ dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs/PUBLIC_KEY/name" --data '{"name":null}'
143
+ dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs/PUBLIC_KEY/members/123/grant" --request POST
144
+ dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs/PUBLIC_KEY/members/123/revoke" --request POST
126
145
  dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs/PUBLIC_KEY/settings/guard" --data '{"value":true}'
146
+ dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs/PUBLIC_KEY/settings/guard" --data '{"value":false}'
127
147
  dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs/PUBLIC_KEY/settings/enclave" --data '{"value":true}'
148
+ dotenvx curl "https://armor.dotenvx.com/api/armor/keypairs/PUBLIC_KEY/settings/enclave" --data '{"value":false}'
128
149
  dotenvx curl "https://armor.dotenvx.com/api/teams"
129
150
  dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM"
130
151
  dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/members"
131
- dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/members"
152
+ dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/members/123/keypairs/PUBLIC_KEY/grant" --request POST
153
+ dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/members/123/keypairs/PUBLIC_KEY/revoke" --request POST
154
+ dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/invitations"
132
155
  dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/invitations" --data '{"email":"person@example.com","role":"member"}'
133
- dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/invitations/123" --request DELETE
156
+ dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/invitations/123/cancel" --request POST
157
+ dotenvx curl "https://armor.dotenvx.com/api/join_requests"
158
+ dotenvx curl "https://armor.dotenvx.com/api/join_requests" --data '{"team":"TEAM"}'
159
+ dotenvx curl "https://armor.dotenvx.com/api/join_requests/123/cancel" --request POST
160
+ dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/join_requests"
161
+ dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/join_requests/123/accept" --data '{"role":"member"}'
162
+ dotenvx curl "https://armor.dotenvx.com/api/teams/TEAM/join_requests/123/decline" --request POST
163
+ dotenvx curl "https://armor.dotenvx.com/api/armor/TEAM/settings/join_requests" --data '{"value":true}'
134
164
  dotenvx curl "https://armor.dotenvx.com/api/logs?team=TEAM"
165
+ dotenvx curl "https://armor.dotenvx.com/api/logs?team=TEAM&events=keypair/access&user=USERNAME&keypair=KEYPAIR_ID&page=1&per=100"
166
+ dotenvx curl "https://armor.dotenvx.com/api/logs?team=TEAM&user=missing&keypair=missing"
135
167
  `
136
168
  }
137
169
 
@@ -0,0 +1,10 @@
1
+ const TEAM_REQUIRED_CODES = new Set([
2
+ 'TEAM_REQUIRED',
3
+ 'DOTENVX_TEAM_REQUIRED'
4
+ ])
5
+
6
+ function isTeamRequiredError (error) {
7
+ return Boolean(error && TEAM_REQUIRED_CODES.has(error.code))
8
+ }
9
+
10
+ module.exports = isTeamRequiredError
@@ -1,6 +1,4 @@
1
1
  const { parse, parseSync } = require('@dotenvx/primitives')
2
- const isNetworkError = require('./isNetworkError')
3
-
4
2
  const SERVER_SIDE_DECRYPTION_REQUIRED = 'SERVER_SIDE_DECRYPTION_REQUIRED'
5
3
 
6
4
  function decryptOptions (error) {
@@ -21,23 +19,30 @@ function parseOptionsWithoutProvider (options) {
21
19
  }
22
20
  }
23
21
 
22
+ function failedKeyAccessFallback (result, error) {
23
+ return {
24
+ ...result,
25
+ errors: [error, ...(result.errors || [])]
26
+ }
27
+ }
28
+
24
29
  async function parseWithDecryptor (src, options = {}) {
25
30
  try {
26
31
  return await parse(src, options)
27
32
  } catch (error) {
28
33
  if (error.code !== SERVER_SIDE_DECRYPTION_REQUIRED || typeof options.decryptor !== 'function') {
29
- throw error
34
+ if (typeof options.provider !== 'function') throw error
35
+
36
+ const result = await parse(src, parseOptionsWithoutProvider(options))
37
+ return failedKeyAccessFallback(result, error)
30
38
  }
31
39
 
32
40
  try {
33
41
  const result = await options.decryptor(src, decryptOptions(error))
34
42
  return await parse(result.src, parseOptionsWithoutProvider(options))
35
43
  } catch (decryptorError) {
36
- if (isNetworkError(decryptorError)) {
37
- return await parse(src, parseOptionsWithoutProvider(options))
38
- }
39
-
40
- throw decryptorError
44
+ const result = await parse(src, parseOptionsWithoutProvider(options))
45
+ return failedKeyAccessFallback(result, decryptorError)
41
46
  }
42
47
  }
43
48
  }
@@ -47,18 +52,18 @@ parseWithDecryptor.sync = function parseWithDecryptorSync (src, options = {}) {
47
52
  return parseSync(src, options)
48
53
  } catch (error) {
49
54
  if (error.code !== SERVER_SIDE_DECRYPTION_REQUIRED || typeof options.decryptor !== 'function') {
50
- throw error
55
+ if (typeof options.provider !== 'function') throw error
56
+
57
+ const result = parseSync(src, parseOptionsWithoutProvider(options))
58
+ return failedKeyAccessFallback(result, error)
51
59
  }
52
60
 
53
61
  try {
54
62
  const result = options.decryptor(src, decryptOptions(error))
55
63
  return parseSync(result.src, parseOptionsWithoutProvider(options))
56
64
  } catch (decryptorError) {
57
- if (isNetworkError(decryptorError)) {
58
- return parseSync(src, parseOptionsWithoutProvider(options))
59
- }
60
-
61
- throw decryptorError
65
+ const result = parseSync(src, parseOptionsWithoutProvider(options))
66
+ return failedKeyAccessFallback(result, decryptorError)
62
67
  }
63
68
  }
64
69
  }
@@ -1,10 +1,27 @@
1
- // Remove [options] from help text. example:
1
+ // Remove [options] from usage/command lines and hide the Options: section.
2
+
3
+ function removeOptionsSection (lines) {
4
+ const start = lines.indexOf('Options:')
5
+ if (start === -1) return
6
+
7
+ let end = start + 1
8
+ while (end < lines.length && lines[end] !== '' && !lines[end].endsWith(':')) {
9
+ end++
10
+ }
11
+
12
+ // drop trailing blank line after the Options block when present
13
+ if (lines[end] === '') end++
14
+
15
+ lines.splice(start, end - start)
16
+ }
2
17
 
3
18
  function removeOptionsHelpParts (lines) {
4
19
  for (let i = 0; i < lines.length; i++) {
5
20
  lines[i] = lines[i].replace(' [options]', '')
6
21
  }
7
22
 
23
+ removeOptionsSection(lines)
24
+
8
25
  let commandsStart = -1
9
26
  for (let i = 0; i < lines.length; i++) {
10
27
  if (lines[i] === 'Commands:') {
package/src/lib/main.d.ts CHANGED
@@ -332,6 +332,16 @@ export function set(
332
332
  ): Promise<SetOutput>;
333
333
 
334
334
  export interface GetOptions {
335
+ /**
336
+ * Specify a custom path if your file containing environment variables is located elsewhere.
337
+ * Can also be an array of strings, specifying multiple paths.
338
+ *
339
+ * @default require('path').resolve(process.cwd(), '.env')
340
+ * @example require('@dotenvx/dotenvx').get('KEY', { path: '/custom/path/to/.env' })
341
+ * @example require('@dotenvx/dotenvx').get('KEY', { path: ['/path/to/first.env', '/path/to/second.env'] })
342
+ */
343
+ path?: string | string[] | URL;
344
+
335
345
  /**
336
346
  * Mask returned values, optionally setting the number of visible characters.
337
347
  * @default false
@@ -32,6 +32,8 @@ function unresolvedEncryptedErrors (parsed) {
32
32
 
33
33
  function decryptErrors (parsed, errors) {
34
34
  const mappedErrors = (errors || []).map(error => {
35
+ if (error instanceof Error) return error
36
+
35
37
  return new Errors({
36
38
  code: error.code,
37
39
  message: error.message,
@@ -4,6 +4,7 @@ const keynames = require('../conventions/keynames')
4
4
  const upsertEnvKey = require('../helpers/upsertEnvKey')
5
5
  const readEnvKey = require('../helpers/readEnvKey')
6
6
  const teamChoicesFromMeta = require('../helpers/teamChoicesFromMeta')
7
+ const isTeamRequiredError = require('../helpers/isTeamRequiredError')
7
8
 
8
9
  class ArmorDown {
9
10
  constructor (hostname, token, devicePublicKey, envFile = '.env', team = undefined) {
@@ -35,7 +36,7 @@ class ArmorDown {
35
36
  try {
36
37
  json = await new PostArmorDown(hostname, token, devicePublicKey, publicKey, undefined).run()
37
38
  } catch (error) {
38
- if (error.code !== 'DOTENVX_TEAM_REQUIRED') {
39
+ if (!isTeamRequiredError(error)) {
39
40
  throw error
40
41
  }
41
42
 
@@ -4,6 +4,7 @@ const upsertEnvKey = require('../helpers/upsertEnvKey')
4
4
  const keynames = require('../conventions/keynames')
5
5
  const readEnvKey = require('../helpers/readEnvKey')
6
6
  const teamChoicesFromMeta = require('../helpers/teamChoicesFromMeta')
7
+ const isTeamRequiredError = require('../helpers/isTeamRequiredError')
7
8
 
8
9
  class ArmorPull {
9
10
  constructor (hostname, token, devicePublicKey, envFile = '.env', team = undefined) {
@@ -35,7 +36,7 @@ class ArmorPull {
35
36
  try {
36
37
  json = await new PostArmorPull(hostname, token, devicePublicKey, publicKey, undefined).run()
37
38
  } catch (error) {
38
- if (error.code !== 'DOTENVX_TEAM_REQUIRED') {
39
+ if (!isTeamRequiredError(error)) {
39
40
  throw error
40
41
  }
41
42
 
@@ -4,6 +4,7 @@ const PostArmorPush = require('../api/postArmorPush')
4
4
  const keynames = require('../conventions/keynames')
5
5
  const readEnvKey = require('../helpers/readEnvKey')
6
6
  const teamChoicesFromMeta = require('../helpers/teamChoicesFromMeta')
7
+ const isTeamRequiredError = require('../helpers/isTeamRequiredError')
7
8
 
8
9
  function publicKeyFromPrivateKey (privateKey) {
9
10
  try {
@@ -42,7 +43,7 @@ class ArmorPush {
42
43
  try {
43
44
  json = await new PostArmorPush(hostname, token, devicePublicKey, privateKey, undefined).run()
44
45
  } catch (error) {
45
- if (error.code !== 'DOTENVX_TEAM_REQUIRED') {
46
+ if (!isTeamRequiredError(error)) {
46
47
  throw error
47
48
  }
48
49
 
@@ -4,6 +4,7 @@ const keynames = require('../conventions/keynames')
4
4
  const removeEnvKey = require('../helpers/removeEnvKey')
5
5
  const readEnvKey = require('../helpers/readEnvKey')
6
6
  const teamChoicesFromMeta = require('../helpers/teamChoicesFromMeta')
7
+ const isTeamRequiredError = require('../helpers/isTeamRequiredError')
7
8
 
8
9
  class ArmorUp {
9
10
  constructor (hostname, token, devicePublicKey, envFile = '.env', team = undefined) {
@@ -37,7 +38,7 @@ class ArmorUp {
37
38
  try {
38
39
  json = await new PostArmorUp(hostname, token, devicePublicKey, publicKey, privateKey, undefined).run()
39
40
  } catch (error) {
40
- if (error.code !== 'DOTENVX_TEAM_REQUIRED') {
41
+ if (!isTeamRequiredError(error)) {
41
42
  throw error
42
43
  }
43
44
 
@@ -13,6 +13,7 @@ const keynames = require('../conventions/keynames')
13
13
  const PostArmorUp = require('../api/postArmorUp')
14
14
  const prompts = require('../helpers/prompts')
15
15
  const teamChoicesFromMeta = require('../helpers/teamChoicesFromMeta')
16
+ const isTeamRequiredError = require('../helpers/isTeamRequiredError')
16
17
  const Session = require('../../db/session')
17
18
 
18
19
  async function selectKeyStorage () {
@@ -110,7 +111,7 @@ async function encryptTransform (options = {}) {
110
111
  try {
111
112
  await new PostArmorUp(hostname, token, devicePublicKey, publicKey, privateKey, undefined).run()
112
113
  } catch (error) {
113
- if (error.code !== 'DOTENVX_TEAM_REQUIRED') {
114
+ if (!isTeamRequiredError(error)) {
114
115
  throw error
115
116
  }
116
117
 
@@ -13,6 +13,7 @@ const Errors = require('../helpers/errors')
13
13
  const PostArmorUp = require('../api/postArmorUp')
14
14
  const prompts = require('../helpers/prompts')
15
15
  const teamChoicesFromMeta = require('../helpers/teamChoicesFromMeta')
16
+ const isTeamRequiredError = require('../helpers/isTeamRequiredError')
16
17
  const Session = require('../../db/session')
17
18
 
18
19
  async function selectKeyStorage () {
@@ -113,7 +114,7 @@ async function setTransform (options = {}) {
113
114
  try {
114
115
  await new PostArmorUp(hostname, token, devicePublicKey, publicKey, privateKey, undefined).run()
115
116
  } catch (error) {
116
- if (error.code !== 'DOTENVX_TEAM_REQUIRED') {
117
+ if (!isTeamRequiredError(error)) {
117
118
  throw error
118
119
  }
119
120