@dotenvx/dotenvx 1.74.3 → 1.75.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 (36) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +70 -22
  3. package/package.json +2 -2
  4. package/src/cli/commands/ext.js +2 -2
  5. package/src/cli/dotenvx.js +29 -27
  6. package/src/cli/examples.js +8 -8
  7. package/src/db/device.js +3 -9
  8. package/src/lib/helpers/append.js +4 -3
  9. package/src/lib/helpers/cryptography/decryptKeyValue.js +10 -10
  10. package/src/lib/helpers/cryptography/encryptValue.js +2 -7
  11. package/src/lib/helpers/cryptography/index.js +1 -5
  12. package/src/lib/helpers/cryptography/localKeypair.js +6 -13
  13. package/src/lib/helpers/decryptDeviceValue.js +2 -5
  14. package/src/lib/helpers/encryptDeviceValue.js +2 -4
  15. package/src/lib/helpers/executeDynamic.js +1 -3
  16. package/src/lib/helpers/installPrecommitHook.js +9 -7
  17. package/src/lib/helpers/keyResolution/keyValues.js +2 -2
  18. package/src/lib/helpers/keyResolution/keyValuesFromEnvSrc.js +8 -3
  19. package/src/lib/helpers/keyResolution/keyValuesSync.js +2 -2
  20. package/src/lib/helpers/keyResolution/readFileKey.js +6 -4
  21. package/src/lib/helpers/keyResolution/readFileKeySync.js +6 -4
  22. package/src/lib/helpers/keypairMetadata.js +2 -2
  23. package/src/lib/helpers/parse.js +2 -2
  24. package/src/lib/helpers/readEnvKey.js +4 -3
  25. package/src/lib/helpers/replace.js +2 -2
  26. package/src/lib/services/armorPush.js +2 -2
  27. package/src/lib/services/decrypt.js +6 -7
  28. package/src/lib/services/encrypt.js +5 -6
  29. package/src/lib/services/genexample.js +16 -4
  30. package/src/lib/services/prebuild.js +5 -5
  31. package/src/lib/services/precommit.js +5 -5
  32. package/src/lib/services/rotate.js +4 -5
  33. package/src/lib/services/sets.js +11 -9
  34. package/src/lib/helpers/cryptography/isEncrypted.js +0 -7
  35. package/src/lib/helpers/dotenvParse.js +0 -55
  36. package/src/lib/helpers/isFullyEncrypted.js +0 -27
package/CHANGELOG.md CHANGED
@@ -2,7 +2,30 @@
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/v1.74.3...main)
5
+ [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.75.1...main)
6
+
7
+ ## [1.75.1](https://github.com/dotenvx/dotenvx/compare/v1.75.0...v1.75.1) (2026-06-20)
8
+
9
+ ### Changed
10
+
11
+ * Publish as named binaries ([#851](https://github.com/dotenvx/dotenvx/pull/851))
12
+
13
+ ### Removed
14
+
15
+ * Remove references to `dotenvx-vlt` (now dotenvx-armor) ([#849](https://github.com/dotenvx/dotenvx/pull/849))
16
+
17
+ ## [1.75.0](https://github.com/dotenvx/dotenvx/compare/v1.74.4...v1.75.0) (2026-06-19)
18
+
19
+ ### Changed
20
+
21
+ * Swap in `@dotenvx/primitives` ([#848](https://github.com/dotenvx/dotenvx/pull/848))
22
+ * `ext` commands become first-class([#847](https://github.com/dotenvx/dotenvx/pull/847))
23
+
24
+ ## [1.74.4](https://github.com/dotenvx/dotenvx/compare/v1.74.3...v1.74.4) (2026-06-19)
25
+
26
+ ### Removed
27
+
28
+ * Remove separately published `@dotenvx/next-env` from this repo ([#846](https://github.com/dotenvx/dotenvx/pull/846))
6
29
 
7
30
  ## [1.74.3](https://github.com/dotenvx/dotenvx/compare/v1.74.2...v1.74.3) (2026-06-19)
8
31
 
package/README.md CHANGED
@@ -150,6 +150,47 @@ $ dotenvx run -- npx tsx index.ts
150
150
  Hello Dotenvx
151
151
  ```
152
152
 
153
+ </details>
154
+ <details><summary>Next.js ▲</summary><br>
155
+
156
+ Install Dotenvx and `@dotenvx/next-env`.
157
+
158
+ ```sh
159
+ $ npm install @dotenvx/dotenvx
160
+ $ npm install @dotenvx/next-env
161
+ ```
162
+
163
+ Override `@next/env` in your `package.json`.
164
+
165
+ ```json
166
+ {
167
+ "overrides": {
168
+ "@next/env": "npm:@dotenvx/next-env"
169
+ }
170
+ }
171
+ ```
172
+
173
+ Encrypt your `.env` file.
174
+
175
+ ```sh
176
+ $ npx dotenvx encrypt
177
+ ◈ encrypted (.env)
178
+ ```
179
+
180
+ Your encrypted secrets are automatically injected and readable in Next.js.
181
+
182
+ ```ts
183
+ import { NextResponse } from 'next/server'
184
+
185
+ export async function GET() {
186
+ return NextResponse.json({
187
+ HELLO: process.env.HELLO
188
+ })
189
+ }
190
+ ```
191
+
192
+ Set `DOTENV_PRIVATE_KEY` in production before deploying.
193
+
153
194
  </details>
154
195
  <details><summary>Cloudflare Workers ⛅️</summary><br>
155
196
 
@@ -1613,7 +1654,7 @@ $ echo "HELLO=Dotenvx" > .env
1613
1654
 
1614
1655
  $ dotenvx encrypt
1615
1656
  ◈ encrypted (.env) + local key (.env.keys)
1616
- ⮕ next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys
1657
+ ⮕ next run [dotenvx gitignore --pattern .env.keys] to gitignore .env.keys
1617
1658
  ⮕ next run [DOTENV_PRIVATE_KEY='122...0b8' dotenvx run -- yourcommand] to test decryption locally
1618
1659
  ```
1619
1660
 
@@ -1628,7 +1669,7 @@ $ echo "HELLO=Production" > .env.production
1628
1669
 
1629
1670
  $ dotenvx encrypt -f .env.production
1630
1671
  ◈ encrypted (.env.production) + local key (.env.keys)
1631
- ⮕ next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys
1672
+ ⮕ next run [dotenvx gitignore --pattern .env.keys] to gitignore .env.keys
1632
1673
  ⮕ next run [DOTENV_PRIVATE_KEY='bff...bc4' dotenvx run -- yourcommand] to test decryption locally
1633
1674
  ```
1634
1675
 
@@ -2121,12 +2162,19 @@ Commands:
2121
2162
  set <KEY> <value> set a single environment variable
2122
2163
  encrypt encrypt .env file(s)
2123
2164
  decrypt decrypt .env file(s)
2165
+ rotate rotate keypair(s) and re-encrypt .env file(s)
2124
2166
  keypair [KEY] print public/private keys for .env file(s)
2125
2167
  ls [directory] print all .env files in a tree structure
2168
+ gitignore append to .gitignore
2169
+ precommit [directory]
2170
+ prevent committing .env files to code
2171
+ prebuild [directory]
2172
+ prevent including .env files in docker
2126
2173
 
2127
- Advanced:
2128
- pro 🏆 pro
2129
- ext 🔌 extensions
2174
+ Professional Security:
2175
+ login log in to move keys off-device, share with your team, and audit access
2176
+ logout log out of connected security features
2177
+ armor ⛨ move private keys off-device [www.dotenvx.com/armor]
2130
2178
  ```
2131
2179
 
2132
2180
  You can get more detailed help per command with `dotenvx help COMMAND`.
@@ -2174,9 +2222,9 @@ X.X.X
2174
2222
 
2175
2223
  </details>
2176
2224
 
2177
- ### Extensions 🔌
2225
+ ### Utilities
2178
2226
 
2179
- CLI extensions.
2227
+ Additional workflow helpers.
2180
2228
 
2181
2229
  <details><summary>`ext genexample`</summary><br>
2182
2230
 
@@ -2233,47 +2281,47 @@ HELLO=""
2233
2281
  ```
2234
2282
 
2235
2283
  </details>
2236
- <details><summary>`ext gitignore`</summary><br>
2284
+ <details><summary>`gitignore`</summary><br>
2237
2285
 
2238
2286
  Gitignore your `.env` files.
2239
2287
 
2240
2288
  ```sh
2241
- $ dotenvx ext gitignore
2289
+ $ dotenvx gitignore
2242
2290
  ▣ ignored .env* (.gitignore)
2243
2291
  ```
2244
2292
 
2245
2293
  </details>
2246
- <details><summary>`ext gitignore --pattern`</summary><br>
2294
+ <details><summary>`gitignore --pattern`</summary><br>
2247
2295
 
2248
2296
  Gitignore specific pattern(s) of `.env` files.
2249
2297
 
2250
2298
  ```sh
2251
- $ dotenvx ext gitignore --pattern .env.keys
2299
+ $ dotenvx gitignore --pattern .env.keys
2252
2300
  ▣ ignored .env.keys (.gitignore)
2253
2301
  ```
2254
2302
 
2255
2303
  </details>
2256
- <details><summary>`ext precommit`</summary><br>
2304
+ <details><summary>`precommit`</summary><br>
2257
2305
 
2258
2306
  Prevent `.env` files from being committed to code.
2259
2307
 
2260
2308
  ```sh
2261
- $ dotenvx ext precommit
2309
+ $ dotenvx precommit
2262
2310
  ▣ .env files (1) protected (encrypted or gitignored)
2263
2311
  ```
2264
2312
 
2265
2313
  </details>
2266
- <details><summary>`ext precommit --install`</summary><br>
2314
+ <details><summary>`precommit --install`</summary><br>
2267
2315
 
2268
2316
  Install a shell script to `.git/hooks/pre-commit` to prevent accidentally committing any `.env` files to source control.
2269
2317
 
2270
2318
  ```sh
2271
- $ dotenvx ext precommit --install
2272
- ▣ dotenvx ext precommit installed [.git/hooks/pre-commit]
2319
+ $ dotenvx precommit --install
2320
+ ▣ dotenvx precommit installed [.git/hooks/pre-commit]
2273
2321
  ```
2274
2322
 
2275
2323
  </details>
2276
- <details><summary>`ext precommit directory`</summary><br>
2324
+ <details><summary>`precommit directory`</summary><br>
2277
2325
 
2278
2326
  Prevent `.env` files from being committed to code inside a specified path to a directory.
2279
2327
 
@@ -2282,12 +2330,12 @@ $ echo "HELLO=Dotenvx" > .env
2282
2330
  $ mkdir -p apps/backend
2283
2331
  $ echo "HELLO=Backend" > apps/backend/.env
2284
2332
 
2285
- $ dotenvx ext precommit apps/backend
2333
+ $ dotenvx precommit apps/backend
2286
2334
  ▣ apps/backend/.env not protected (encrypted or gitignored)
2287
2335
  ```
2288
2336
 
2289
2337
  </details>
2290
- <details><summary>`ext prebuild`</summary><br>
2338
+ <details><summary>`prebuild`</summary><br>
2291
2339
 
2292
2340
  Prevent `.env` files from being built into your docker containers.
2293
2341
 
@@ -2302,12 +2350,12 @@ COPY --from=dotenv/dotenvx:latest /usr/local/bin/dotenvx /bin/local/bin
2302
2350
 
2303
2351
  # ... orther container commands
2304
2352
 
2305
- RUN dotenvx ext prebuild
2353
+ RUN dotenvx prebuild
2306
2354
  CMD ["/usr/local/bin/dotenvx", "run", "--", "node", "index.js"]
2307
2355
  ```
2308
2356
 
2309
2357
  </details>
2310
- <details><summary>`ext prebuild directory`</summary><br>
2358
+ <details><summary>`prebuild directory`</summary><br>
2311
2359
 
2312
2360
  Prevent `.env` files from being built into your docker containers inside a specified path to a directory.
2313
2361
 
@@ -2322,7 +2370,7 @@ COPY --from=dotenv/dotenvx:latest /usr/local/bin/dotenvx /bin/local/bin
2322
2370
 
2323
2371
  # ... orther container commands
2324
2372
 
2325
- RUN dotenvx ext prebuild apps/backend
2373
+ RUN dotenvx prebuild apps/backend
2326
2374
  CMD ["/usr/local/bin/dotenvx", "run", "--", "node", "apps/backend/index.js"]
2327
2375
  ```
2328
2376
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.74.3",
2
+ "version": "1.75.1",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a secure dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -43,10 +43,10 @@
43
43
  },
44
44
  "funding": "https://dotenvx.com",
45
45
  "dependencies": {
46
+ "@dotenvx/primitives": "^0.8.0",
46
47
  "commander": "^11.1.0",
47
48
  "conf": "^10.2.0",
48
49
  "dotenv": "^17.2.1",
49
- "eciesjs": "^0.4.10",
50
50
  "enquirer": "^2.4.1",
51
51
  "env-paths": "^2.2.1",
52
52
  "execa": "^5.1.1",
@@ -39,7 +39,7 @@ ext.command('genexample')
39
39
 
40
40
  // dotenvx ext gitignore
41
41
  ext.command('gitignore')
42
- .description('append to .gitignore file (and if existing, .dockerignore, .npmignore, and .vercelignore)')
42
+ .description('append to .gitignore')
43
43
  .addHelpText('after', examples.gitignore)
44
44
  .option('--pattern <patterns...>', 'pattern(s) to gitignore', ['.env*'])
45
45
  .action(function (...args) {
@@ -48,7 +48,7 @@ ext.command('gitignore')
48
48
 
49
49
  // dotenvx ext prebuild
50
50
  ext.command('prebuild')
51
- .description('prevent including .env files in docker builds')
51
+ .description('prevent including .env files in docker')
52
52
  .addHelpText('after', examples.prebuild)
53
53
  .argument('[directory]', 'directory to prevent including .env files from', '.')
54
54
  .action(function (...args) {
@@ -194,6 +194,34 @@ program.command('ls')
194
194
  return require('./actions/ls').apply(this, args)
195
195
  })
196
196
 
197
+ // dotenvx gitignore
198
+ program.command('gitignore')
199
+ .description('append to .gitignore')
200
+ .addHelpText('after', examples.gitignore)
201
+ .option('--pattern <patterns...>', 'pattern(s) to gitignore', ['.env*'])
202
+ .action(function (...args) {
203
+ return require('./actions/ext/gitignore').apply(this, args)
204
+ })
205
+
206
+ // dotenvx precommit
207
+ program.command('precommit')
208
+ .description('prevent committing .env files to code')
209
+ .addHelpText('after', examples.precommit)
210
+ .argument('[directory]', 'directory to prevent committing .env files from', '.')
211
+ .option('-i, --install', 'install to .git/hooks/pre-commit')
212
+ .action(function (...args) {
213
+ return require('./actions/ext/precommit').apply(this, args)
214
+ })
215
+
216
+ // dotenvx prebuild
217
+ program.command('prebuild')
218
+ .description('prevent including .env files in docker')
219
+ .addHelpText('after', examples.prebuild)
220
+ .argument('[directory]', 'directory to prevent including .env files from', '.')
221
+ .action(function (...args) {
222
+ return require('./actions/ext/prebuild').apply(this, args)
223
+ })
224
+
197
225
  // dotenvx doctor
198
226
  program.command('doctor', { hidden: true })
199
227
  .description('scan for dotenv loaders')
@@ -242,7 +270,6 @@ program.addHelpText('after', 'Professional Security: ')
242
270
  program.addHelpText('after', ' login log in to move keys off-device, share with your team, and audit access')
243
271
  program.addHelpText('after', ' logout log out of connected security features')
244
272
  program.addHelpText('after', ' armor ⛨ move private keys off-device [www.dotenvx.com/armor]')
245
- program.addHelpText('after', ' ext ⊕ extensions')
246
273
 
247
274
  // dotenvx armor
248
275
  require('./commands/armor')(program.command('armor', { hidden: true }))
@@ -250,30 +277,6 @@ require('./commands/armor')(program.command('armor', { hidden: true }))
250
277
  // dotenvx ext
251
278
  program.addCommand(require('./commands/ext'))
252
279
 
253
- //
254
- // MOVED
255
- //
256
- program.command('prebuild')
257
- .description('DEPRECATED: moved to [dotenvx ext prebuild]')
258
- .addHelpText('after', examples.prebuild)
259
- .argument('[directory]', 'directory to prevent including .env files from', '.')
260
- .action(function (...args) {
261
- logger.warn('DEPRECATION NOTICE: [prebuild] has moved to [dotenvx ext prebuild]')
262
-
263
- require('./actions/ext/prebuild').apply(this, args)
264
- })
265
-
266
- program.command('precommit')
267
- .description('DEPRECATED: moved to [dotenvx ext precommit]')
268
- .addHelpText('after', examples.precommit)
269
- .argument('[directory]', 'directory to prevent committing .env files from', '.')
270
- .option('-i, --install', 'install to .git/hooks/pre-commit')
271
- .action(function (...args) {
272
- logger.warn('DEPRECATION NOTICE: [precommit] has moved to [dotenvx ext precommit]')
273
-
274
- require('./actions/ext/precommit').apply(this, args)
275
- })
276
-
277
280
  // override helpInformation to hide DEPRECATED and 'ext' commands
278
281
  program.helpInformation = function () {
279
282
  const originalHelp = Command.prototype.helpInformation.call(this)
@@ -286,8 +289,7 @@ program.helpInformation = function () {
286
289
  const filteredLines = lines.filter(line =>
287
290
  !line.includes('DEPRECATED') &&
288
291
  !line.includes('help [command]') &&
289
- !line.includes('🔌 extensions') &&
290
- !/^\s*ls\b/.test(line)
292
+ !line.includes('🔌 extensions')
291
293
  )
292
294
 
293
295
  return filteredLines.join('\n')
@@ -27,14 +27,14 @@ const precommit = function () {
27
27
  Examples:
28
28
 
29
29
  \`\`\`
30
- $ dotenvx ext precommit
31
- $ dotenvx ext precommit --install
30
+ $ dotenvx precommit
31
+ $ dotenvx precommit --install
32
32
  \`\`\`
33
33
 
34
34
  Try it:
35
35
 
36
36
  \`\`\`
37
- $ dotenvx ext precommit
37
+ $ dotenvx precommit
38
38
  [dotenvx@0.45.0][precommit] success
39
39
  \`\`\`
40
40
  `
@@ -45,13 +45,13 @@ const prebuild = function () {
45
45
  Examples:
46
46
 
47
47
  \`\`\`
48
- $ dotenvx ext prebuild
48
+ $ dotenvx prebuild
49
49
  \`\`\`
50
50
 
51
51
  Try it:
52
52
 
53
53
  \`\`\`
54
- $ dotenvx ext prebuild
54
+ $ dotenvx prebuild
55
55
  [dotenvx@0.10.0][prebuild] success
56
56
  \`\`\`
57
57
  `
@@ -62,14 +62,14 @@ const gitignore = function () {
62
62
  Examples:
63
63
 
64
64
  \`\`\`
65
- $ dotenvx ext gitignore
66
- $ dotenvx ext gitignore --pattern .env.keys
65
+ $ dotenvx gitignore
66
+ $ dotenvx gitignore --pattern .env.keys
67
67
  \`\`\`
68
68
 
69
69
  Try it:
70
70
 
71
71
  \`\`\`
72
- $ dotenvx ext gitignore
72
+ $ dotenvx gitignore
73
73
  ▣ ignored .env* (.gitignore)
74
74
  \`\`\`
75
75
  `
package/src/db/device.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const Conf = require('conf')
2
- const { PrivateKey } = require('eciesjs')
2
+ const { derive, keypair } = require('@dotenvx/primitives')
3
3
 
4
4
  const encryptDeviceValue = require('./../lib/helpers/encryptDeviceValue')
5
5
  const decryptDeviceValue = require('./../lib/helpers/decryptDeviceValue')
@@ -38,9 +38,7 @@ class Device {
38
38
  return currentPrivateKey
39
39
  }
40
40
 
41
- // generate privateKey for the first time
42
- const kp = new PrivateKey()
43
- const _privateKey = kp.secret.toString('hex')
41
+ const _privateKey = keypair().privateKey
44
42
 
45
43
  this.store.set('private_key/1', _privateKey)
46
44
 
@@ -54,11 +52,7 @@ class Device {
54
52
  return ''
55
53
  }
56
54
 
57
- // create keyPair object from hex string
58
- const _privateKey = new PrivateKey(Buffer.from(privateKeyHex, 'hex'))
59
-
60
- // compute publicKey from privateKey
61
- return _privateKey.publicKey.toHex()
55
+ return derive(privateKeyHex)
62
56
  }
63
57
 
64
58
  encrypt (value) {
@@ -1,5 +1,5 @@
1
1
  const quotes = require('./quotes')
2
- const dotenvParse = require('./dotenvParse')
2
+ const { scan } = require('@dotenvx/primitives')
3
3
  const escapeForRegex = require('./escapeForRegex')
4
4
  const escapeDollarSigns = require('./escapeDollarSigns')
5
5
 
@@ -7,11 +7,12 @@ function append (src, key, appendValue) {
7
7
  let output
8
8
  let newPart = ''
9
9
 
10
- const parsed = dotenvParse(src, true, true) // skip expanding \n and skip converting \r\n
10
+ const { parsed } = scan(src, { expandDoubleQuotedNewlines: false, convertWindowsNewlines: false })
11
11
  const _quotes = quotes(src)
12
12
  if (Object.prototype.hasOwnProperty.call(parsed, key)) {
13
13
  const quote = _quotes[key]
14
- const originalValue = parsed[key]
14
+ const values = parsed[key]
15
+ const originalValue = values[values.length - 1]
15
16
 
16
17
  newPart += `${key}=${quote}${originalValue},${appendValue}${quote}`
17
18
 
@@ -1,9 +1,13 @@
1
- const { decrypt } = require('eciesjs')
1
+ const { decrypt } = require('@dotenvx/primitives')
2
2
 
3
3
  const Errors = require('./../errors')
4
4
 
5
5
  const PREFIX = 'encrypted:'
6
6
 
7
+ function primitiveMessage (error) {
8
+ return error.message.replace(/^\[[A-Z_]+\] /, '')
9
+ }
10
+
7
11
  function decryptKeyValue (key, value, privateKeyName, privateKey) {
8
12
  let decryptedValue
9
13
  let decryptionError
@@ -18,23 +22,19 @@ function decryptKeyValue (key, value, privateKeyName, privateKey) {
18
22
  } else {
19
23
  const privateKeys = privateKey.split(',')
20
24
  for (const privKey of privateKeys) {
21
- const secret = Buffer.from(privKey, 'hex')
22
- const encoded = value.substring(PREFIX.length)
23
- const ciphertext = Buffer.from(encoded, 'base64')
24
-
25
25
  try {
26
- decryptedValue = decrypt(secret, ciphertext).toString()
26
+ decryptedValue = decrypt(privKey, value)
27
27
  decryptionError = null // reset to null error (scenario for multiple private keys)
28
28
  break
29
29
  } catch (e) {
30
- if (e.message === 'Invalid private key') {
30
+ if (e.code === 'INVALID_PRIVATE_KEY') {
31
31
  decryptionError = new Errors({ key, privateKeyName, privateKey }).invalidPrivateKey()
32
- } else if (e.message === 'Unsupported state or unable to authenticate data') {
32
+ } else if (e.code === 'WRONG_PRIVATE_KEY') {
33
33
  decryptionError = new Errors({ key, privateKeyName, privateKey }).wrongPrivateKey()
34
- } else if (e.message === 'Point of length 65 was invalid. Expected 33 compressed bytes or 65 uncompressed bytes') {
34
+ } else if (e.code === 'MALFORMED_ENCRYPTED_DATA' || primitiveMessage(e).startsWith('bad point:')) {
35
35
  decryptionError = new Errors({ key, privateKeyName, privateKey }).malformedEncryptedData()
36
36
  } else {
37
- decryptionError = new Errors({ key, privateKeyName, privateKey, message: e.message }).decryptionFailed()
37
+ decryptionError = new Errors({ key, privateKeyName, privateKey, message: primitiveMessage(e) }).decryptionFailed()
38
38
  }
39
39
  }
40
40
  }
@@ -1,12 +1,7 @@
1
- const { encrypt } = require('eciesjs')
2
-
3
- const PREFIX = 'encrypted:'
1
+ const { encrypt } = require('@dotenvx/primitives')
4
2
 
5
3
  function encryptValue (value, publicKey) {
6
- const ciphertext = encrypt(publicKey, Buffer.from(value))
7
- const encoded = Buffer.from(ciphertext, 'hex').toString('base64') // base64 encode ciphertext
8
-
9
- return `${PREFIX}${encoded}`
4
+ return encrypt(publicKey, value)
10
5
  }
11
6
 
12
7
  module.exports = encryptValue
@@ -4,15 +4,11 @@ module.exports = {
4
4
  localKeypair: require('./localKeypair'),
5
5
  encryptValue: require('./encryptValue'),
6
6
  decryptKeyValue: require('./decryptKeyValue'),
7
- isEncrypted: require('./isEncrypted'),
8
7
  isPublicKey: require('./isPublicKey'),
9
8
  mutateKeysSrc: require('./mutateKeysSrc'),
10
9
  mutateKeysSrcSync: require('./mutateKeysSrcSync'),
11
10
  provision: require('./provision'),
12
11
  provisionSync: require('./provisionSync'),
13
12
  provisionWithPrivateKey: require('./provisionWithPrivateKey'),
14
- mutateSrc: require('./mutateSrc'),
15
-
16
- // other
17
- isFullyEncrypted: require('./../isFullyEncrypted')
13
+ mutateSrc: require('./mutateSrc')
18
14
  }
@@ -1,21 +1,14 @@
1
- const { PrivateKey } = require('eciesjs')
1
+ const { derive, keypair } = require('@dotenvx/primitives')
2
2
 
3
3
  function localKeypair (existingPrivateKey) {
4
- let kp
5
-
6
4
  if (existingPrivateKey) {
7
- kp = new PrivateKey(Buffer.from(existingPrivateKey, 'hex'))
8
- } else {
9
- kp = new PrivateKey()
5
+ return {
6
+ publicKey: derive(existingPrivateKey),
7
+ privateKey: existingPrivateKey
8
+ }
10
9
  }
11
10
 
12
- const publicKey = kp.publicKey.toHex()
13
- const privateKey = kp.secret.toString('hex')
14
-
15
- return {
16
- publicKey,
17
- privateKey
18
- }
11
+ return keypair()
19
12
  }
20
13
 
21
14
  module.exports = localKeypair
@@ -1,10 +1,7 @@
1
- const { decrypt } = require('eciesjs')
1
+ const { decrypt } = require('@dotenvx/primitives')
2
2
 
3
3
  function decryptDeviceValue (value, privateKey) {
4
- const secret = Buffer.from(privateKey, 'hex')
5
- const ciphertext = Buffer.from(value, 'base64')
6
-
7
- return decrypt(secret, ciphertext).toString()
4
+ return decrypt(privateKey, value, { prefix: false })
8
5
  }
9
6
 
10
7
  module.exports = decryptDeviceValue
@@ -1,9 +1,7 @@
1
- const { encrypt } = require('eciesjs')
1
+ const { encrypt } = require('@dotenvx/primitives')
2
2
 
3
3
  function encryptDeviceValue (value, publicKey) {
4
- const ciphertext = encrypt(publicKey, Buffer.from(value))
5
-
6
- return Buffer.from(ciphertext, 'hex').toString('base64')
4
+ return encrypt(publicKey, value, { prefix: false })
7
5
  }
8
6
 
9
7
  module.exports = encryptDeviceValue
@@ -45,7 +45,6 @@ function dynamicAttempts (command, forwardedArgs) {
45
45
  if (command === 'vlt') {
46
46
  return [
47
47
  ['dotenvx-armor', forwardedArgs],
48
- ['dotenvx-vlt', forwardedArgs],
49
48
  ['dotenvx-ops', forwardedArgs]
50
49
  ]
51
50
  }
@@ -53,8 +52,7 @@ function dynamicAttempts (command, forwardedArgs) {
53
52
  if (command === 'ops') {
54
53
  return [
55
54
  ['dotenvx-armor', forwardedArgs],
56
- ['dotenvx-ops', forwardedArgs],
57
- ['dotenvx-vlt', forwardedArgs]
55
+ ['dotenvx-ops', forwardedArgs]
58
56
  ]
59
57
  }
60
58
 
@@ -6,10 +6,10 @@ const HOOK_SCRIPT = `#!/bin/sh
6
6
 
7
7
  if command -v dotenvx 2>&1 >/dev/null
8
8
  then
9
- dotenvx ext precommit
9
+ dotenvx precommit
10
10
  elif npx dotenvx -V >/dev/null 2>&1
11
11
  then
12
- npx dotenvx ext precommit
12
+ npx dotenvx precommit
13
13
  else
14
14
  if [ -t 2 ]; then
15
15
  RED="$(printf '\\033[31m')"
@@ -19,7 +19,7 @@ else
19
19
  RESET=""
20
20
  fi
21
21
 
22
- printf "%s☠ 'dotenvx ext precommit' command not found (.git/hooks/precommit) fix: [curl -sfS https://dotenvx.sh | sh]%s\n" "$RED" "$RESET" >&2
22
+ printf "%s☠ 'dotenvx precommit' command not found (.git/hooks/precommit) fix: [curl -sfS https://dotenvx.sh | sh]%s\n" "$RED" "$RESET" >&2
23
23
  exit 1
24
24
  fi
25
25
  `
@@ -35,17 +35,19 @@ class InstallPrecommitHook {
35
35
  try {
36
36
  // Check if the pre-commit file already exists
37
37
  if (this._exists()) {
38
+ const currentHook = this._currentHook()
39
+
38
40
  // Check if 'dotenvx precommit' already exists in the file
39
- if (this._currentHook().includes('dotenvx ext precommit')) {
41
+ if (currentHook.includes('dotenvx precommit') || currentHook.includes('dotenvx ext precommit')) {
40
42
  // do nothing
41
- successMessage = `▣ dotenvx ext precommit exists [${this.hookPath}]`
43
+ successMessage = `▣ dotenvx precommit exists [${this.hookPath}]`
42
44
  } else {
43
45
  this._appendHook()
44
- successMessage = `▣ dotenvx ext precommit appended [${this.hookPath}]`
46
+ successMessage = `▣ dotenvx precommit appended [${this.hookPath}]`
45
47
  }
46
48
  } else {
47
49
  this._createHook()
48
- successMessage = `▣ dotenvx ext precommit installed [${this.hookPath}]`
50
+ successMessage = `▣ dotenvx precommit installed [${this.hookPath}]`
49
51
  }
50
52
 
51
53
  return {
@@ -1,7 +1,7 @@
1
1
  const path = require('path')
2
+ const { scan } = require('@dotenvx/primitives')
2
3
 
3
4
  const fsx = require('./../fsx')
4
- const dotenvParse = require('./../dotenvParse')
5
5
  const keyNamesForEnvFile = require('./keyNamesForEnvFile')
6
6
  const readProcessKey = require('./readProcessKey')
7
7
  const readFileKey = require('./readFileKey')
@@ -16,7 +16,7 @@ async function invertForPrivateKeyName (filepath) {
16
16
  }
17
17
 
18
18
  const envSrc = await fsx.readFileX(filepath)
19
- const envParsed = dotenvParse(envSrc)
19
+ const { parsed: envParsed } = scan(envSrc)
20
20
 
21
21
  let publicKeyName
22
22
  for (const keyName of Object.keys(envParsed)) {
@@ -1,6 +1,6 @@
1
1
  const path = require('path')
2
+ const { scan } = require('@dotenvx/primitives')
2
3
 
3
- const dotenvParse = require('./../dotenvParse')
4
4
  const readFileKeySync = require('./readFileKeySync')
5
5
  const armorKeypairSync = require('../cryptography/armorKeypairSync')
6
6
 
@@ -23,14 +23,19 @@ function publicKeyNameFromEnvSrc (envParsed) {
23
23
  return null
24
24
  }
25
25
 
26
+ function finalValue (parsed, keyName) {
27
+ const values = parsed[keyName]
28
+ return values ? values[values.length - 1] : undefined
29
+ }
30
+
26
31
  function keyValuesFromEnvSrc (src, privateKeyName = null, opts = {}) {
27
32
  let keysFilepath = opts.keysFilepath || null
28
33
  const noArmor = opts.noArmor === true
29
34
  const processEnv = opts.processEnv || process.env
30
- const envParsed = dotenvParse(src)
35
+ const { parsed: envParsed } = scan(src)
31
36
 
32
37
  const publicKeyName = publicKeyNameFromEnvSrc(envParsed)
33
- const publicKeyValue = publicKeyName ? readProcessKey(processEnv, publicKeyName) || envParsed[publicKeyName] || null : null
38
+ const publicKeyValue = publicKeyName ? readProcessKey(processEnv, publicKeyName) || finalValue(envParsed, publicKeyName) || null : null
34
39
 
35
40
  if (!privateKeyName && publicKeyName) {
36
41
  privateKeyName = publicKeyName.replace(PUBLIC_KEY_SCHEMA, PRIVATE_KEY_SCHEMA)
@@ -1,7 +1,7 @@
1
1
  const path = require('path')
2
+ const { scan } = require('@dotenvx/primitives')
2
3
 
3
4
  const fsx = require('./../fsx')
4
- const dotenvParse = require('./../dotenvParse')
5
5
  const keyNamesForEnvFile = require('./keyNamesForEnvFile')
6
6
  const readProcessKey = require('./readProcessKey')
7
7
  const readFileKeySync = require('./readFileKeySync')
@@ -16,7 +16,7 @@ function invertForPrivateKeyName (filepath) {
16
16
  }
17
17
 
18
18
  const envSrc = fsx.readFileXSync(filepath)
19
- const envParsed = dotenvParse(envSrc)
19
+ const { parsed: envParsed } = scan(envSrc)
20
20
 
21
21
  let publicKeyName
22
22
  for (const keyName of Object.keys(envParsed)) {
@@ -1,5 +1,5 @@
1
1
  const fsx = require('./../fsx')
2
- const dotenvParse = require('./../dotenvParse')
2
+ const { scan } = require('@dotenvx/primitives')
3
3
 
4
4
  async function readFileKey (keyName, filepath) {
5
5
  if (!(await fsx.exists(filepath))) {
@@ -7,10 +7,12 @@ async function readFileKey (keyName, filepath) {
7
7
  }
8
8
 
9
9
  const src = await fsx.readFileX(filepath)
10
- const parsed = dotenvParse(src)
10
+ const { parsed } = scan(src)
11
+ const values = parsed[keyName]
11
12
 
12
- if (parsed[keyName] && parsed[keyName].length > 0) {
13
- return parsed[keyName]
13
+ if (values && values.length > 0) {
14
+ const value = values[values.length - 1]
15
+ return value || undefined
14
16
  }
15
17
  }
16
18
 
@@ -1,13 +1,15 @@
1
1
  const fsx = require('./../fsx')
2
- const dotenvParse = require('./../dotenvParse')
2
+ const { scan } = require('@dotenvx/primitives')
3
3
 
4
4
  function readFileKeySync (keyName, filepath) {
5
5
  if (fsx.existsSync(filepath)) {
6
6
  const src = fsx.readFileXSync(filepath)
7
- const parsed = dotenvParse(src)
7
+ const { parsed } = scan(src)
8
+ const values = parsed[keyName]
8
9
 
9
- if (parsed[keyName] && parsed[keyName].length > 0) {
10
- return parsed[keyName]
10
+ if (values && values.length > 0) {
11
+ const value = values[values.length - 1]
12
+ return value || undefined
11
13
  }
12
14
  }
13
15
  }
@@ -1,7 +1,7 @@
1
1
  const path = require('path')
2
2
  const fs = require('fs')
3
3
  const execa = require('execa')
4
- const dotenvParse = require('./dotenvParse')
4
+ const { scan } = require('@dotenvx/primitives')
5
5
  const canonicalEnvFilename = require('./canonicalEnvFilename')
6
6
  const environment = require('./envResolution/environment')
7
7
  const sanitizeCommandForMetadata = require('./sanitizeCommandForMetadata')
@@ -56,7 +56,7 @@ function envKeys (envFile) {
56
56
  return null
57
57
  }
58
58
 
59
- const parsed = dotenvParse(src)
59
+ const { parsed } = scan(src)
60
60
 
61
61
  if (!parsed || Object.keys(parsed).length === 0) return null
62
62
 
@@ -1,5 +1,5 @@
1
1
  const decryptKeyValue = require('./cryptography/decryptKeyValue')
2
- const isEncrypted = require('./cryptography/isEncrypted')
2
+ const { encrypted } = require('@dotenvx/primitives')
3
3
  const evalKeyValue = require('./evalKeyValue')
4
4
  const resolveEscapeSequences = require('./resolveEscapeSequences')
5
5
 
@@ -45,7 +45,7 @@ class Parse {
45
45
  this.errors.push(e)
46
46
  }
47
47
 
48
- const encryptedPrefixed = isEncrypted(this.parsed[key]) // do not eval encrypted prefix
48
+ const encryptedPrefixed = encrypted(this.parsed[key]) // do not eval encrypted prefix
49
49
 
50
50
  // eval empty, double, or backticks
51
51
  let evaled = false
@@ -1,5 +1,5 @@
1
1
  const fs = require('fs')
2
- const dotenvParse = require('./dotenvParse')
2
+ const { scan } = require('@dotenvx/primitives')
3
3
  const Errors = require('./errors')
4
4
 
5
5
  function ignored (error, options) {
@@ -17,8 +17,9 @@ function readEnvKey (key, filepath, options = {}) {
17
17
  return undefined
18
18
  }
19
19
 
20
- const parsed = dotenvParse(src)
21
- const value = parsed[key]
20
+ const { parsed } = scan(src)
21
+ const values = parsed[key]
22
+ const value = values ? values[values.length - 1] : undefined
22
23
  if (value === undefined) {
23
24
  const error = new Errors({ key }).missingKey()
24
25
  if (ignored(error, options)) return undefined
@@ -1,4 +1,4 @@
1
- const dotenvParse = require('./dotenvParse')
1
+ const { scan } = require('@dotenvx/primitives')
2
2
  const escapeForRegex = require('./escapeForRegex')
3
3
 
4
4
  function replaceExistingValue (src, key, originalValue, replaceValue) {
@@ -45,7 +45,7 @@ function replace (src, key, replaceValue) {
45
45
  let output
46
46
  let newPart = ''
47
47
 
48
- const parsed = dotenvParse(src, true, true, true) // skip expanding \n and skip converting \r\n
48
+ const { parsed } = scan(src, { expandDoubleQuotedNewlines: false, convertWindowsNewlines: false })
49
49
  if (Object.prototype.hasOwnProperty.call(parsed, key)) {
50
50
  const allValues = parsed[key]
51
51
  let duplicateOutput = src
@@ -1,4 +1,4 @@
1
- const { PrivateKey } = require('eciesjs')
1
+ const { derive } = require('@dotenvx/primitives')
2
2
  const prompts = require('../helpers/prompts')
3
3
  const PostArmorPush = require('../api/postArmorPush')
4
4
  const keyNamesForEnvFile = require('../helpers/keyResolution/keyNamesForEnvFile')
@@ -7,7 +7,7 @@ const teamChoicesFromMeta = require('../helpers/teamChoicesFromMeta')
7
7
 
8
8
  function publicKeyFromPrivateKey (privateKey) {
9
9
  try {
10
- return new PrivateKey(Buffer.from(privateKey, 'hex')).publicKey.toHex()
10
+ return derive(privateKey)
11
11
  } catch {
12
12
  return ''
13
13
  }
@@ -1,6 +1,7 @@
1
1
  const fsx = require('./../helpers/fsx')
2
2
  const path = require('path')
3
3
  const picomatch = require('picomatch')
4
+ const { encrypted, scan } = require('@dotenvx/primitives')
4
5
 
5
6
  const TYPE_ENV_FILE = 'envFile'
6
7
 
@@ -16,12 +17,10 @@ const {
16
17
  } = require('./../helpers/keyResolution')
17
18
 
18
19
  const {
19
- decryptKeyValue,
20
- isEncrypted
20
+ decryptKeyValue
21
21
  } = require('./../helpers/cryptography')
22
22
 
23
23
  const replace = require('./../helpers/replace')
24
- const dotenvParse = require('./../helpers/dotenvParse')
25
24
  const detectEncoding = require('./../helpers/detectEncoding')
26
25
 
27
26
  class Decrypt {
@@ -75,7 +74,7 @@ class Decrypt {
75
74
  try {
76
75
  const encoding = await detectEncoding(filepath)
77
76
  let envSrc = await fsx.readFileX(filepath, { encoding })
78
- const envParsed = dotenvParse(envSrc, false, false, true)
77
+ const envParsed = scan(envSrc).parsed
79
78
 
80
79
  const { privateKeyName } = keyNamesForEnvFile(envFilepath)
81
80
  const { privateKeyValue, privateKeySource } = await keyValues(envFilepath, {
@@ -101,12 +100,12 @@ class Decrypt {
101
100
  continue
102
101
  }
103
102
 
104
- const encrypted = values.some(value => isEncrypted(value))
105
- if (encrypted) {
103
+ const hasEncrypted = values.some(value => encrypted(value))
104
+ if (hasEncrypted) {
106
105
  row.keys.push(key) // track key(s)
107
106
 
108
107
  const decryptedValues = values.map(value => {
109
- if (!isEncrypted(value)) {
108
+ if (!encrypted(value)) {
110
109
  return value
111
110
  }
112
111
 
@@ -1,6 +1,7 @@
1
1
  const fsx = require('./../helpers/fsx')
2
2
  const path = require('path')
3
3
  const picomatch = require('picomatch')
4
+ const { encrypted, scan } = require('@dotenvx/primitives')
4
5
 
5
6
  const TYPE_ENV_FILE = 'envFile'
6
7
 
@@ -17,14 +18,12 @@ const {
17
18
 
18
19
  const {
19
20
  encryptValue,
20
- isEncrypted,
21
21
  isPublicKey,
22
22
  provision,
23
23
  provisionWithPrivateKey
24
24
  } = require('./../helpers/cryptography')
25
25
 
26
26
  const replace = require('./../helpers/replace')
27
- const dotenvParse = require('./../helpers/dotenvParse')
28
27
  const detectEncoding = require('./../helpers/detectEncoding')
29
28
  const SAMPLE_ENV_KIT = require('./../helpers/kits/sample')
30
29
 
@@ -97,7 +96,7 @@ class Encrypt {
97
96
  row.kitCreated = 'sample'
98
97
  row.changed = true
99
98
  }
100
- const envParsed = dotenvParse(envSrc, false, false, true)
99
+ const envParsed = scan(envSrc).parsed
101
100
 
102
101
  let publicKey
103
102
  let privateKey
@@ -151,12 +150,12 @@ class Encrypt {
151
150
  continue
152
151
  }
153
152
 
154
- const encrypted = values.every(value => isEncrypted(value) || isPublicKey(key))
155
- if (!encrypted) {
153
+ const fullyEncrypted = values.every(value => encrypted(value) || isPublicKey(key))
154
+ if (!fullyEncrypted) {
156
155
  row.keys.push(key) // track key(s)
157
156
 
158
157
  const encryptedValues = values.map(value => {
159
- if (isEncrypted(value) || isPublicKey(key)) {
158
+ if (encrypted(value) || isPublicKey(key)) {
160
159
  return value
161
160
  }
162
161
 
@@ -4,7 +4,19 @@ const path = require('path')
4
4
  const Errors = require('../helpers/errors')
5
5
  const findEnvFiles = require('../helpers/findEnvFiles')
6
6
  const replace = require('../helpers/replace')
7
- const dotenvParse = require('../helpers/dotenvParse')
7
+ const { scan } = require('@dotenvx/primitives')
8
+
9
+ function finalValues (src) {
10
+ const { parsed } = scan(src)
11
+ const result = {}
12
+
13
+ for (const key in parsed) {
14
+ const values = parsed[key]
15
+ result[key] = values[values.length - 1]
16
+ }
17
+
18
+ return result
19
+ }
8
20
 
9
21
  class Genexample {
10
22
  constructor (directory = '.', envFile) {
@@ -34,13 +46,13 @@ class Genexample {
34
46
  const filepath = path.resolve(this.directory, envFilepath)
35
47
  if (!fsx.existsSync(filepath)) {
36
48
  const error = new Errors({ envFilepath, filepath }).missingEnvFile()
37
- error.help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx genexample]`
49
+ error.help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx ext genexample]`
38
50
  throw error
39
51
  }
40
52
 
41
53
  // get the original src
42
54
  let src = fsx.readFileXSync(filepath)
43
- const parsed = dotenvParse(src)
55
+ const parsed = finalValues(src)
44
56
  for (const key in parsed) {
45
57
  // used later
46
58
  keys.add(key)
@@ -65,7 +77,7 @@ class Genexample {
65
77
  // it already exists (which means the user might have it modified a way in which they prefer, so replace exampleSrc with their existing .env.example)
66
78
  exampleSrc = fsx.readFileXSync(this.exampleFilepath)
67
79
 
68
- const parsed = dotenvParse(exampleSrc)
80
+ const parsed = finalValues(exampleSrc)
69
81
  for (const key of [...keys]) {
70
82
  if (key in parsed) {
71
83
  preExisted[key] = parsed[key]
@@ -5,8 +5,8 @@ const ignore = require('ignore')
5
5
 
6
6
  const Ls = require('../services/ls')
7
7
  const Errors = require('../helpers/errors')
8
+ const { sealed } = require('@dotenvx/primitives')
8
9
 
9
- const isFullyEncrypted = require('./../helpers/isFullyEncrypted')
10
10
  const MISSING_DOCKERIGNORE = '.env.keys' // by default only ignore .env.keys. all other .env* files COULD be included - as long as they are encrypted
11
11
 
12
12
  class Prebuild {
@@ -47,22 +47,22 @@ class Prebuild {
47
47
  if (file === '.env.example' || file === '.env.x') {
48
48
  const warning = new Errors({
49
49
  message: `${file} ignored (should not be)`,
50
- help: `fix: [dotenvx ext gitignore --pattern !${file}]`
50
+ help: `fix: [dotenvx gitignore --pattern !${file}]`
51
51
  }).custom()
52
52
  warnings.push(warning)
53
53
  }
54
54
  } else {
55
55
  if (file !== '.env.example' && file !== '.env.x') {
56
56
  const src = fsx.readFileXSync(file)
57
- const encrypted = isFullyEncrypted(src)
57
+ const encrypted = sealed(src)
58
58
 
59
59
  // if contents are encrypted don't raise an error
60
60
  if (!encrypted) {
61
61
  let errorMsg = `${file} not encrypted/dockerignored`
62
- let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx ext gitignore --pattern ${file}]`
62
+ let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx gitignore --pattern ${file}]`
63
63
  if (file.includes('.env.keys')) {
64
64
  errorMsg = `${file} not dockerignored`
65
- errorHelp = `fix: [dotenvx ext gitignore --pattern ${file}]`
65
+ errorHelp = `fix: [dotenvx gitignore --pattern ${file}]`
66
66
  }
67
67
 
68
68
  throw new Errors({ message: errorMsg, help: errorHelp }).custom()
@@ -4,8 +4,8 @@ const path = require('path')
4
4
  const ignore = require('ignore')
5
5
 
6
6
  const Ls = require('../services/ls')
7
+ const { sealed } = require('@dotenvx/primitives')
7
8
 
8
- const isFullyEncrypted = require('./../helpers/isFullyEncrypted')
9
9
  const InstallPrecommitHook = require('./../helpers/installPrecommitHook')
10
10
  const Errors = require('./../helpers/errors')
11
11
  const childProcess = require('child_process')
@@ -63,22 +63,22 @@ class Precommit {
63
63
  if (file === '.env.example' || file === '.env.x') {
64
64
  const warning = new Errors({
65
65
  message: `${file} ignored (should not be)`,
66
- help: `fix: [dotenvx ext gitignore --pattern !${file}]`
66
+ help: `fix: [dotenvx gitignore --pattern !${file}]`
67
67
  }).custom()
68
68
  warnings.push(warning)
69
69
  }
70
70
  } else {
71
71
  if (file !== '.env.example' && file !== '.env.x') {
72
72
  const src = fsx.readFileXSync(file)
73
- const encrypted = isFullyEncrypted(src)
73
+ const encrypted = sealed(src)
74
74
 
75
75
  // if contents are encrypted don't raise an error
76
76
  if (!encrypted) {
77
77
  let errorMsg = `${file} not encrypted/gitignored`
78
- let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx ext gitignore --pattern ${file}]`
78
+ let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx gitignore --pattern ${file}]`
79
79
  if (file.includes('.env.keys')) {
80
80
  errorMsg = `${file} not gitignored`
81
- errorHelp = `fix: [dotenvx ext gitignore --pattern ${file}]`
81
+ errorHelp = `fix: [dotenvx gitignore --pattern ${file}]`
82
82
  }
83
83
 
84
84
  throw new Errors({ message: errorMsg, help: errorHelp }).custom()
@@ -1,6 +1,7 @@
1
1
  const fsx = require('./../helpers/fsx')
2
2
  const path = require('path')
3
3
  const picomatch = require('picomatch')
4
+ const { encrypted, scan } = require('@dotenvx/primitives')
4
5
 
5
6
  const TYPE_ENV_FILE = 'envFile'
6
7
 
@@ -19,13 +20,11 @@ const {
19
20
  armorKeypair,
20
21
  localKeypair,
21
22
  encryptValue,
22
- decryptKeyValue,
23
- isEncrypted
23
+ decryptKeyValue
24
24
  } = require('./../helpers/cryptography')
25
25
 
26
26
  const append = require('./../helpers/append')
27
27
  const replace = require('./../helpers/replace')
28
- const dotenvParse = require('./../helpers/dotenvParse')
29
28
  const detectEncoding = require('./../helpers/detectEncoding')
30
29
 
31
30
  class Rotate {
@@ -81,7 +80,7 @@ class Rotate {
81
80
  try {
82
81
  const encoding = await detectEncoding(filepath)
83
82
  let envSrc = await fsx.readFileX(filepath, { encoding })
84
- const envParsed = dotenvParse(envSrc, false, false, true)
83
+ const envParsed = scan(envSrc).parsed
85
84
 
86
85
  const { publicKeyName, privateKeyName } = keyNamesForEnvFile(envFilepath)
87
86
  const { privateKeyValue } = await keyValues(envFilepath, {
@@ -134,7 +133,7 @@ class Rotate {
134
133
  continue
135
134
  }
136
135
 
137
- const value = [...values].reverse().find(value => isEncrypted(value))
136
+ const value = [...values].reverse().find(value => encrypted(value))
138
137
  if (value) { // only re-encrypt those already encrypted
139
138
  row.keys.push(key) // track key(s)
140
139
 
@@ -1,5 +1,6 @@
1
1
  const fsx = require('./../helpers/fsx')
2
2
  const path = require('path')
3
+ const { encrypted, scan } = require('@dotenvx/primitives')
3
4
 
4
5
  const TYPE_ENV_FILE = 'envFile'
5
6
 
@@ -18,19 +19,22 @@ const {
18
19
  const {
19
20
  encryptValue,
20
21
  decryptKeyValue,
21
- isEncrypted,
22
22
  provision,
23
23
  provisionSync,
24
24
  provisionWithPrivateKey
25
25
  } = require('./../helpers/cryptography')
26
26
 
27
27
  const replace = require('./../helpers/replace')
28
- const dotenvParse = require('./../helpers/dotenvParse')
29
28
  const detectEncoding = require('./../helpers/detectEncoding')
30
29
  const detectEncodingSync = require('./../helpers/detectEncodingSync')
31
30
 
32
31
  function allValuesForKey (envSrc, key) {
33
- return dotenvParse(envSrc, false, false, true)[key] || []
32
+ return scan(envSrc).parsed[key] || []
33
+ }
34
+
35
+ function finalValueForKey (envSrc, key) {
36
+ const values = allValuesForKey(envSrc, key)
37
+ return values.length > 0 ? values[values.length - 1] : null
34
38
  }
35
39
 
36
40
  class Sets {
@@ -119,12 +123,11 @@ class Sets {
119
123
  seededWithInitialKey = true
120
124
  }
121
125
 
122
- const envParsed = dotenvParse(envSrc)
123
- row.originalValue = envParsed[row.key] || null
126
+ row.originalValue = finalValueForKey(envSrc, row.key)
124
127
  if (seededWithInitialKey) {
125
128
  row.originalValue = null
126
129
  }
127
- const wasPlainText = !isEncrypted(row.originalValue)
130
+ const wasPlainText = !encrypted(row.originalValue)
128
131
  this.readableFilepaths.add(envFilepath)
129
132
 
130
133
  if (this.encrypt) {
@@ -236,12 +239,11 @@ class Sets {
236
239
  seededWithInitialKey = true
237
240
  }
238
241
 
239
- const envParsed = dotenvParse(envSrc)
240
- row.originalValue = envParsed[row.key] || null
242
+ row.originalValue = finalValueForKey(envSrc, row.key)
241
243
  if (seededWithInitialKey) {
242
244
  row.originalValue = null
243
245
  }
244
- const wasPlainText = !isEncrypted(row.originalValue)
246
+ const wasPlainText = !encrypted(row.originalValue)
245
247
  this.readableFilepaths.add(envFilepath)
246
248
 
247
249
  if (this.encrypt) {
@@ -1,7 +0,0 @@
1
- const ENCRYPTION_PATTERN = /^encrypted:.+/
2
-
3
- function isEncrypted (value) {
4
- return ENCRYPTION_PATTERN.test(value)
5
- }
6
-
7
- module.exports = isEncrypted
@@ -1,55 +0,0 @@
1
- // historical dotenv.parse - https://github.com/motdotla/dotenv)
2
- const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
3
-
4
- function dotenvParse (src, skipExpandForDoubleQuotes = false, skipConvertingWindowsNewlines = false, collectAllValues = false) {
5
- const obj = {}
6
-
7
- // Convert buffer to string
8
- let lines = src.toString()
9
-
10
- // Convert line breaks to same format
11
- if (!skipConvertingWindowsNewlines) {
12
- lines = lines.replace(/\r\n?/mg, '\n')
13
- }
14
-
15
- let match
16
- while ((match = LINE.exec(lines)) != null) {
17
- const key = match[1]
18
-
19
- // Default undefined or null to empty string
20
- let value = (match[2] || '')
21
-
22
- // Remove whitespace
23
- value = value.trim()
24
-
25
- // Check if double quoted
26
- const maybeQuote = value[0]
27
-
28
- // Remove surrounding quotes
29
- value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2')
30
-
31
- // Expand newlines if double quoted
32
- if (maybeQuote === '"' && !skipExpandForDoubleQuotes) {
33
- value = value.replace(/\\n/g, '\n') // newline
34
- value = value.replace(/\\r/g, '\r') // carriage return
35
- value = value.replace(/\\t/g, '\t') // tabs
36
- }
37
-
38
- if (collectAllValues) {
39
- // handle scenario where user mistakenly includes plaintext duplicate in .env:
40
- //
41
- // # .env
42
- // HELLO="World"
43
- // HELLO="encrypted:1234"
44
- obj[key] = obj[key] || []
45
- obj[key].push(value)
46
- } else {
47
- // Add to object
48
- obj[key] = value
49
- }
50
- }
51
-
52
- return obj
53
- }
54
-
55
- module.exports = dotenvParse
@@ -1,27 +0,0 @@
1
- const dotenvParse = require('./dotenvParse')
2
- const isEncrypted = require('./cryptography/isEncrypted')
3
- const isPublicKey = require('./cryptography/isPublicKey')
4
-
5
- function isFullyEncrypted (src) {
6
- const parsed = dotenvParse(src, false, false, true) // collect all values
7
-
8
- for (const [key, values] of Object.entries(parsed)) {
9
- // handle scenario where user mistakenly includes plaintext duplicate in .env:
10
- //
11
- // # .env
12
- // HELLO="World"
13
- // HELLO="encrypted:1234"
14
- //
15
- // key => [value1, ...]
16
- for (const value of values) {
17
- const result = isEncrypted(value) || isPublicKey(key)
18
- if (!result) {
19
- return false
20
- }
21
- }
22
- }
23
-
24
- return true
25
- }
26
-
27
- module.exports = isFullyEncrypted