@dotenvx/dotenvx-ops 0.15.6

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 (51) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/LICENSE +71 -0
  3. package/README.md +9 -0
  4. package/package.json +66 -0
  5. package/src/cli/actions/login.js +118 -0
  6. package/src/cli/actions/logout.js +42 -0
  7. package/src/cli/actions/observe.js +33 -0
  8. package/src/cli/actions/settings/device.js +24 -0
  9. package/src/cli/actions/settings/hostname.js +20 -0
  10. package/src/cli/actions/settings/token.js +24 -0
  11. package/src/cli/actions/settings/username.js +22 -0
  12. package/src/cli/actions/status.js +13 -0
  13. package/src/cli/actions/sync.js +39 -0
  14. package/src/cli/commands/settings.js +39 -0
  15. package/src/cli/dotenvx-ops.js +93 -0
  16. package/src/db/device.js +73 -0
  17. package/src/db/session.js +119 -0
  18. package/src/lib/api/postLogout.js +34 -0
  19. package/src/lib/api/postOauthDeviceCode.js +42 -0
  20. package/src/lib/api/postOauthToken.js +38 -0
  21. package/src/lib/api/postObserve.js +59 -0
  22. package/src/lib/api/postSync.js +65 -0
  23. package/src/lib/helpers/buildApiError.js +13 -0
  24. package/src/lib/helpers/buildOauthError.js +13 -0
  25. package/src/lib/helpers/clipboardy/fallbacks/linux/xsel +0 -0
  26. package/src/lib/helpers/clipboardy/fallbacks/windows/clipboard_i686.exe +0 -0
  27. package/src/lib/helpers/clipboardy/fallbacks/windows/clipboard_x86_64.exe +0 -0
  28. package/src/lib/helpers/clipboardy/linux.js +57 -0
  29. package/src/lib/helpers/clipboardy/macos.js +14 -0
  30. package/src/lib/helpers/clipboardy/termux.js +41 -0
  31. package/src/lib/helpers/clipboardy/windows.js +16 -0
  32. package/src/lib/helpers/clipboardy.js +51 -0
  33. package/src/lib/helpers/confirm.js +17 -0
  34. package/src/lib/helpers/createSpinner.js +101 -0
  35. package/src/lib/helpers/decryptValue.js +10 -0
  36. package/src/lib/helpers/encryptValue.js +9 -0
  37. package/src/lib/helpers/errors.js +30 -0
  38. package/src/lib/helpers/formatCode.js +11 -0
  39. package/src/lib/helpers/gitBranch.js +13 -0
  40. package/src/lib/helpers/gitUrl.js +13 -0
  41. package/src/lib/helpers/http.js +17 -0
  42. package/src/lib/helpers/jsonToEnv.js +7 -0
  43. package/src/lib/helpers/mask.js +12 -0
  44. package/src/lib/helpers/packageJson.js +3 -0
  45. package/src/lib/helpers/smartMask.js +11 -0
  46. package/src/lib/helpers/truncate.js +10 -0
  47. package/src/lib/main.d.ts +129 -0
  48. package/src/lib/main.js +47 -0
  49. package/src/lib/services/logout.js +28 -0
  50. package/src/lib/services/status.js +11 -0
  51. package/src/lib/services/sync.js +94 -0
@@ -0,0 +1,51 @@
1
+ // based on "clipboardy" by Sindre Sorhus (https://github.com/sindresorhus/clipboardy)
2
+ // licensed under the MIT License (see [license](https://github.com/sindresorhus/clipboardy/blob/v2.3.0/license) file for details).
3
+
4
+ 'use strict'
5
+ const isWSL = require('is-wsl')
6
+ const termux = require('./clipboardy/termux.js')
7
+ const linux = require('./clipboardy/linux.js')
8
+ const macos = require('./clipboardy/macos.js')
9
+ const windows = require('./clipboardy/windows.js')
10
+
11
+ const platformLib = (() => {
12
+ switch (process.platform) {
13
+ case 'darwin':
14
+ return macos
15
+ case 'win32':
16
+ return windows
17
+ case 'android':
18
+ if (process.env.PREFIX !== '/data/data/com.termux/files/usr') {
19
+ throw new Error('You need to install Termux for this module to work on Android: https://termux.com')
20
+ }
21
+
22
+ return termux
23
+ default:
24
+ // `process.platform === 'linux'` for WSL.
25
+ if (isWSL) {
26
+ return windows
27
+ }
28
+
29
+ return linux
30
+ }
31
+ })()
32
+
33
+ exports.write = async text => {
34
+ if (typeof text !== 'string') {
35
+ throw new TypeError(`Expected a string, got ${typeof text}`)
36
+ }
37
+
38
+ await platformLib.copy({ input: text })
39
+ }
40
+
41
+ exports.read = async () => platformLib.paste({ stripEof: false })
42
+
43
+ exports.writeSync = text => {
44
+ if (typeof text !== 'string') {
45
+ throw new TypeError(`Expected a string, got ${typeof text}`)
46
+ }
47
+
48
+ platformLib.copySync({ input: text })
49
+ }
50
+
51
+ exports.readSync = () => platformLib.pasteSync({ stripEof: false }).stdout
@@ -0,0 +1,17 @@
1
+ const { ConfirmPrompt, isCancel } = require('@clack/core')
2
+
3
+ module.exports = async (opts) => {
4
+ const prompt = new ConfirmPrompt({
5
+ active: 'Y',
6
+ inactive: 'N',
7
+ initialValue: true,
8
+ render () {
9
+ return `${opts.message} (Y/n)`
10
+ }
11
+ })
12
+
13
+ const result = await prompt.prompt()
14
+ if (isCancel(result)) process.exit(0)
15
+
16
+ return result
17
+ }
@@ -0,0 +1,101 @@
1
+ const { getColor, bold } = require('@dotenvx/dotenvx')
2
+
3
+ const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
4
+ const HIDE_CURSOR = '\u001B[?25l'
5
+ const SHOW_CURSOR = '\u001B[?25h'
6
+ const CLEAR_LINE = '\r\x1b[K'
7
+ const SYMBOL_INFO = 'ℹ'
8
+ const SYMBOL_WARN = '⚠'
9
+ const SYMBOL_ERROR = '✖'
10
+ const SYMBOL_SUCCESS = '✔'
11
+
12
+ class Spinner {
13
+ text
14
+ interval
15
+ frameIndex = 0
16
+ symbol = getColor('blue')(FRAMES[0])
17
+
18
+ constructor (text) {
19
+ this.text = text
20
+ }
21
+
22
+ start (text) {
23
+ if (text) {
24
+ this.text = text
25
+ }
26
+ this.render()
27
+ this.interval = setInterval(() => this.tick(), 50)
28
+ }
29
+
30
+ tick () {
31
+ this.symbol = getColor('blue')(FRAMES[this.frameIndex++])
32
+ if (this.frameIndex >= FRAMES.length) this.frameIndex = 0
33
+ this.render()
34
+ }
35
+
36
+ render () {
37
+ process.stdout.write(CLEAR_LINE + HIDE_CURSOR + (this.symbol ? this.symbol + ' ' : '') + this.text)
38
+ }
39
+
40
+ succeed (text) {
41
+ if (text) {
42
+ this.text = text
43
+ }
44
+ this.symbol = getColor('green')(SYMBOL_SUCCESS)
45
+ this._end()
46
+ }
47
+
48
+ info (text) {
49
+ if (text) {
50
+ this.text = text
51
+ }
52
+ this.symbol = getColor('blue')(SYMBOL_INFO)
53
+ this._end()
54
+ }
55
+
56
+ warn (text) {
57
+ if (text) {
58
+ this.text = text
59
+ }
60
+ this.symbol = getColor('orangered')(SYMBOL_WARN)
61
+ this._end()
62
+ }
63
+
64
+ fail (text) {
65
+ if (text) {
66
+ this.text = text
67
+ }
68
+ this.symbol = getColor('red')(SYMBOL_ERROR)
69
+ this._end()
70
+ }
71
+
72
+ stop () {
73
+ this.symbol = ''
74
+ this.text = ''
75
+ clearInterval(this.interval)
76
+ process.stdout.write(CLEAR_LINE + HIDE_CURSOR + (this.symbol ? this.symbol + ' ' : '') + this.text)
77
+ process.stdout.write(SHOW_CURSOR)
78
+ }
79
+
80
+ _end () {
81
+ this.render()
82
+ clearInterval(this.interval)
83
+ process.stdout.write(SHOW_CURSOR + '\n')
84
+ }
85
+ }
86
+
87
+ const createSpinner = (initialMessage = '') => {
88
+ const spinner = new Spinner(initialMessage)
89
+
90
+ return {
91
+ start: (message) => spinner.start(message),
92
+ succeed: (message) => spinner.succeed(getColor('green')(message)),
93
+ warn: (message) => spinner.warn(getColor('orangered')(message)),
94
+ info: (message) => spinner.info(getColor('blue')(message)),
95
+ done: (message) => spinner.succeed(message),
96
+ fail: (message) => spinner.fail(bold(getColor('red')(message))),
97
+ stop: () => spinner.stop()
98
+ }
99
+ }
100
+
101
+ module.exports = { createSpinner, Spinner }
@@ -0,0 +1,10 @@
1
+ const { decrypt } = require('eciesjs')
2
+
3
+ function decryptValue (value, privateKey) {
4
+ const secret = Buffer.from(privateKey, 'hex')
5
+ const ciphertext = Buffer.from(value, 'base64')
6
+
7
+ return decrypt(secret, ciphertext).toString()
8
+ }
9
+
10
+ module.exports = decryptValue
@@ -0,0 +1,9 @@
1
+ const { encrypt } = require('eciesjs')
2
+
3
+ function encryptValue (value, publicKey) {
4
+ const ciphertext = encrypt(publicKey, Buffer.from(value))
5
+
6
+ return Buffer.from(ciphertext, 'hex').toString('base64') // base64 encode ciphertext
7
+ }
8
+
9
+ module.exports = encryptValue
@@ -0,0 +1,30 @@
1
+ class Errors {
2
+ constructor (options = {}) {
3
+ this.filename = options.filename
4
+ this.filepath = options.filepath
5
+ }
6
+
7
+ econnrefused () {
8
+ const code = 'ECONNREFUSED'
9
+ const message = `[${code}] connection refused`
10
+ const help = `[${code}] check your internet connection`
11
+
12
+ const e = new Error(message)
13
+ e.code = code
14
+ e.help = help
15
+ return e
16
+ }
17
+
18
+ envXFileMissing () {
19
+ const code = 'ENV_X_FILE_MISSING'
20
+ const message = `[${code}] missing ${this.filename} file (${this.filepath})`
21
+ const help = `[${code}] https://github.com/dotenvx/dotenvx/issues/664`
22
+
23
+ const e = new Error(message)
24
+ e.code = code
25
+ e.help = help
26
+ return e
27
+ }
28
+ }
29
+
30
+ module.exports = Errors
@@ -0,0 +1,11 @@
1
+ function formatCode (str) {
2
+ const parts = []
3
+
4
+ for (let i = 0; i < str.length; i += 4) {
5
+ parts.push(str.substring(i, i + 4))
6
+ }
7
+
8
+ return parts.join('-')
9
+ }
10
+
11
+ module.exports = formatCode
@@ -0,0 +1,13 @@
1
+ const execa = require('execa')
2
+
3
+ function gitBranch () {
4
+ try {
5
+ const raw = execa.sync('git', ['rev-parse', '--abbrev-ref', 'HEAD'])
6
+
7
+ return raw.stdout.toString().trim()
8
+ } catch (_error) {
9
+ return null
10
+ }
11
+ }
12
+
13
+ module.exports = gitBranch
@@ -0,0 +1,13 @@
1
+ const execa = require('execa')
2
+
3
+ function gitUrl () {
4
+ try {
5
+ const raw = execa.sync('git', ['remote', 'get-url', 'origin'])
6
+
7
+ return raw.stdout.toString().trim()
8
+ } catch (_error) {
9
+ return null
10
+ }
11
+ }
12
+
13
+ module.exports = gitUrl
@@ -0,0 +1,17 @@
1
+ const { request } = require('undici')
2
+
3
+ const Errors = require('./errors')
4
+
5
+ async function http (url, opts = {}) {
6
+ try {
7
+ return await request(url, opts)
8
+ } catch (err) {
9
+ if (err.code === 'econnrefused') {
10
+ throw new Errors().econnrefused()
11
+ }
12
+
13
+ throw err
14
+ }
15
+ }
16
+
17
+ module.exports = { http }
@@ -0,0 +1,7 @@
1
+ function jsonToEnv (json) {
2
+ return Object.entries(json).map(function ([key, value]) {
3
+ return key + '=' + `"${value}"`
4
+ }).join('\n')
5
+ }
6
+
7
+ module.exports = jsonToEnv
@@ -0,0 +1,12 @@
1
+ function mask (str, showChar = 7) {
2
+ if (!str || str.length < 1) {
3
+ return ''
4
+ }
5
+
6
+ const visiblePart = str.slice(0, showChar)
7
+ const maskedPart = '*'.repeat(str.length - showChar)
8
+
9
+ return visiblePart + maskedPart
10
+ }
11
+
12
+ module.exports = mask
@@ -0,0 +1,3 @@
1
+ const { name, version, description } = require('../../../package.json')
2
+
3
+ module.exports = { name, version, description }
@@ -0,0 +1,11 @@
1
+ const mask = require('./mask')
2
+
3
+ function smartMask (str, unmask = false, showChar = 7) {
4
+ if (unmask) {
5
+ return str
6
+ } else {
7
+ return mask(str, showChar)
8
+ }
9
+ }
10
+
11
+ module.exports = smartMask
@@ -0,0 +1,10 @@
1
+ function truncate (str, showChar = 7) {
2
+ if (str && str.length > 0) {
3
+ const visiblePart = str.slice(0, showChar)
4
+ return visiblePart + '…'
5
+ } else {
6
+ return ''
7
+ }
8
+ }
9
+
10
+ module.exports = truncate
@@ -0,0 +1,129 @@
1
+ import type { URL } from 'url';
2
+
3
+ /**
4
+ * Observes a dotenvx run
5
+ *
6
+ * @see https://dotenvx.com/docs
7
+ * @param payload - observability payload string
8
+ * @returns the original payload string
9
+ */
10
+ export function observe(payload: string): string;
11
+
12
+ export interface DotenvConfigOptions {
13
+ /**
14
+ * Specify a custom path if your file containing environment variables is located elsewhere.
15
+ * Can also be an array of strings, specifying multiple paths.
16
+ *
17
+ * @default require('path').resolve(process.cwd(), '.env')
18
+ * @example require('@dotenvx/dotenvx').config({ path: '/custom/path/to/.env' })
19
+ * @example require('@dotenvx/dotenvx').config({ path: ['/path/to/first.env', '/path/to/second.env'] })
20
+ */
21
+ path?: string | string[] | URL;
22
+
23
+ /**
24
+ * Specify the encoding of your file containing environment variables.
25
+ *
26
+ * @default 'utf8'
27
+ * @example require('@dotenvx/dotenvx').config({ encoding: 'latin1' })
28
+ */
29
+ encoding?: string;
30
+
31
+ /**
32
+ * Override any environment variables that have already been set on your machine with values from your .env file.
33
+ * @default false
34
+ * @example require('@dotenvx/dotenvx').config({ overload: true })
35
+ * @alias overload
36
+ */
37
+ overload?: boolean;
38
+
39
+ /**
40
+ * @default false
41
+ * @alias override
42
+ */
43
+ override?: boolean;
44
+
45
+ /**
46
+ * Throw immediately if an error is encountered - like a missing .env file.
47
+ * @default false
48
+ * @example require('@dotenvx/dotenvx').config({ strict: true })
49
+ */
50
+ strict?: boolean;
51
+
52
+ /**
53
+ * Suppress specific errors like MISSING_ENV_FILE. The error keys can be found
54
+ * in src/lib/helpers/errors.js
55
+ * @default []
56
+ * @example require('@dotenvx/dotenvx').config({ ignore: ['MISSING_ENV_FILE'] })
57
+ */
58
+ ignore?: string[];
59
+
60
+ /**
61
+ * Specify an object to write your secrets to. Defaults to process.env environment variables.
62
+ *
63
+ * @default process.env
64
+ * @example const processEnv = {}; require('@dotenvx/dotenvx').config({ processEnv: processEnv })
65
+ */
66
+ processEnv?: DotenvPopulateInput;
67
+
68
+ /**
69
+ * Customize the path to your .env.keys file. This is useful with monorepos.
70
+ * @default []
71
+ * @example require('@dotenvx/dotenvx').config({ envKeysFile: '../../.env.keys'} })
72
+ */
73
+ envKeysFile?: string;
74
+
75
+ /**
76
+ * Pass the DOTENV_KEY directly to config options. Defaults to looking for process.env.DOTENV_KEY environment variable. Note this only applies to decrypting .env.vault files. If passed as null or undefined, or not passed at all, dotenv falls back to its traditional job of parsing a .env file.
77
+ *
78
+ * @default undefined
79
+ * @example require('@dotenvx/dotenvx').config({ DOTENV_KEY: 'dotenv://:key_1234…@dotenvx.com/vault/.env.vault?environment=production' })
80
+ */
81
+ DOTENV_KEY?: string;
82
+
83
+ /**
84
+ * Load a .env convention (available conventions: 'nextjs, flow')
85
+ */
86
+ convention?: string;
87
+
88
+ /**
89
+ * Turn on logging to help debug why certain keys or values are not being set as you expect.
90
+ *
91
+ * @default false
92
+ * @example require('@dotenvx/dotenvx').config({ debug: process.env.DEBUG })
93
+ */
94
+ debug?: boolean;
95
+
96
+ verbose?: boolean;
97
+
98
+ quiet?: boolean;
99
+
100
+ logLevel?:
101
+ | 'error'
102
+ | 'warn'
103
+ | 'success'
104
+ | 'successv'
105
+ | 'info'
106
+ | 'help'
107
+ | 'verbose'
108
+ | 'debug';
109
+ }
110
+
111
+ export interface DotenvConfigOutput {
112
+ error?: Error;
113
+ parsed?: DotenvParseOutput;
114
+ }
115
+
116
+ export interface DotenvPopulateInput {
117
+ [name: string]: string;
118
+ }
119
+
120
+ /**
121
+ * Loads `.env` file contents into process.env by default. If `DOTENV_KEY` is present, it smartly attempts to load encrypted `.env.vault` file contents into process.env.
122
+ *
123
+ * @see https://dotenvx.com/docs
124
+ *
125
+ * @param options - additional options. example: `{ path: './custom/path', encoding: 'latin1', debug: true, overload: false }`
126
+ * @returns an object with a `parsed` key if successful or `error` key if an error occurred. example: { parsed: { KEY: 'value' } }
127
+ *
128
+ */
129
+ export function config(options?: DotenvConfigOptions): DotenvConfigOutput;
@@ -0,0 +1,47 @@
1
+ const si = require('systeminformation')
2
+ const dotenvx = require('@dotenvx/dotenvx')
3
+
4
+ const Session = require('./../db/session')
5
+ const PostObserve = require('./api/postObserve')
6
+
7
+ const gitUrl = require('./helpers/gitUrl')
8
+ const gitBranch = require('./helpers/gitBranch')
9
+
10
+ const observe = async function (encoded, options = {}) {
11
+ const sesh = new Session() // TODO: handle scenario where constructor fails
12
+
13
+ let hostname = process.env.DOTENVX_OPS_HOSTNAME || process.env.DOTENVX_RADAR_HOSTNAME || options.hostname
14
+ if (!hostname) {
15
+ hostname = sesh.hostname()
16
+ }
17
+
18
+ let token = process.env.DOTENVX_OPS_TOKEN || process.env.DOTENVX_RADAR_TOKEN || options.token
19
+ if (!token) {
20
+ token = sesh.token()
21
+ }
22
+
23
+ const _pwd = process.cwd()
24
+ const _gitUrl = gitUrl()
25
+ const _gitBranch = gitBranch()
26
+
27
+ const system = await si.system()
28
+ const _systemUuid = system.uuid
29
+
30
+ const osInfo = await si.osInfo()
31
+ const _osPlatform = osInfo.platform
32
+ const _osArch = osInfo.arch
33
+
34
+ const json = await new PostObserve(hostname, token, encoded, _pwd, _gitUrl, _gitBranch, _systemUuid, _osPlatform, _osArch).run()
35
+
36
+ return json
37
+ }
38
+
39
+ const config = function (options = {}) {
40
+ return dotenvx.config(options)
41
+ }
42
+
43
+ module.exports = {
44
+ observe,
45
+ // dotenv proxies
46
+ config
47
+ }
@@ -0,0 +1,28 @@
1
+ const Session = require('./../../db/session')
2
+
3
+ // api calls
4
+ const PostLogout = require('./../../lib/api/postLogout')
5
+
6
+ class Logout {
7
+ constructor (hostname) {
8
+ this.hostname = hostname
9
+ }
10
+
11
+ async run () {
12
+ const sesh = new Session()
13
+ const token = sesh.token()
14
+
15
+ const data = await new PostLogout(this.hostname, token).run()
16
+
17
+ const id = data.id
18
+ const username = data.username
19
+ const accessToken = data.access_token
20
+ const settingsDevicesUrl = `${this.hostname}/settings/devices`
21
+
22
+ sesh.logout(this.hostname, id, accessToken)
23
+
24
+ return { username, accessToken, settingsDevicesUrl }
25
+ }
26
+ }
27
+
28
+ module.exports = Logout
@@ -0,0 +1,11 @@
1
+ const Session = require('./../../db/session')
2
+
3
+ class Status {
4
+ run () {
5
+ const sesh = new Session()
6
+ const status = sesh.status()
7
+ return { status }
8
+ }
9
+ }
10
+
11
+ module.exports = Status
@@ -0,0 +1,94 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const si = require('systeminformation')
4
+ const dotenvx = require('@dotenvx/dotenvx')
5
+
6
+ const Session = require('./../../db/session')
7
+
8
+ const gitUrl = require('./../helpers/gitUrl')
9
+ const gitBranch = require('./../helpers/gitBranch')
10
+ const Errors = require('./../helpers/errors')
11
+
12
+ // api calls
13
+ const PostSync = require('./../api/postSync')
14
+
15
+ class Sync {
16
+ constructor (hostname) {
17
+ this.hostname = hostname
18
+ this.cwd = process.cwd()
19
+ }
20
+
21
+ async run () {
22
+ const sesh = new Session()
23
+ const token = sesh.token()
24
+ const devicePublicKey = sesh.devicePublicKey()
25
+
26
+ // required
27
+ // need to get all .env files here somehow
28
+ const files = this._files()
29
+ const payload = { files }
30
+ const encoded = Buffer.from(JSON.stringify(payload)).toString('base64')
31
+ const dotenvxProjectId = this._dotenvxProjectId()
32
+
33
+ // optional
34
+ const _pwd = this.cwd
35
+ const _gitUrl = gitUrl()
36
+ const _gitBranch = gitBranch()
37
+
38
+ const system = await si.system()
39
+ const _systemUuid = system.uuid
40
+
41
+ const osInfo = await si.osInfo()
42
+ const _osPlatform = osInfo.platform
43
+ const _osArch = osInfo.arch
44
+
45
+ const data = await new PostSync(this.hostname, token, devicePublicKey, encoded, dotenvxProjectId, _pwd, _gitUrl, _gitBranch, _systemUuid, _osPlatform, _osArch).run()
46
+
47
+ return {
48
+ id: data.id,
49
+ dotenvxProjectId: data.dotenvx_project_id,
50
+ projectUsernameName: data.project_username_name
51
+ }
52
+ }
53
+
54
+ _dotenvxProjectId () {
55
+ // 1. Prefer environment variable
56
+ if (process.env.DOTENVX_PROJECT_ID && process.env.DOTENVX_PROJECT_ID.trim()) {
57
+ return process.env.DOTENVX_PROJECT_ID.trim()
58
+ }
59
+
60
+ // 2. Otherwise, parse .env.x contents
61
+ const filepath = path.join(this.cwd, '.env.x')
62
+ // file must exist
63
+ if (!fs.existsSync(filepath)) {
64
+ const filename = path.basename(filepath)
65
+ throw new Errors({ filename, filepath }).envXFileMissing()
66
+ }
67
+
68
+ if (fs.existsSync(filepath)) {
69
+ const src = fs.readFileSync(filepath, 'utf8')
70
+ const parsed = dotenvx.parse(src)
71
+ if (parsed.DOTENVX_PROJECT_ID && parsed.DOTENVX_PROJECT_ID.trim()) {
72
+ return parsed.DOTENVX_PROJECT_ID.trim()
73
+ }
74
+ }
75
+
76
+ // 3. Nothing found
77
+ return null
78
+ }
79
+
80
+ _files () {
81
+ const out = []
82
+ const filepaths = dotenvx.ls(this.cwd)
83
+
84
+ for (const fp of filepaths) {
85
+ const abs = path.join(this.cwd, fp)
86
+ const src = fs.readFileSync(abs, 'utf8')
87
+ out.push({ filepath: fp, src })
88
+ }
89
+
90
+ return out
91
+ }
92
+ }
93
+
94
+ module.exports = Sync