@dotenvx/dotenvx-vlt 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/CHANGELOG.md +652 -0
  2. package/LICENSE +71 -0
  3. package/README.md +11 -0
  4. package/package.json +77 -0
  5. package/src/cli/actions/armor/down.js +37 -0
  6. package/src/cli/actions/armor/move.js +42 -0
  7. package/src/cli/actions/armor/pull.js +37 -0
  8. package/src/cli/actions/armor/push.js +37 -0
  9. package/src/cli/actions/armor/rotate.js +25 -0
  10. package/src/cli/actions/armor/up.js +37 -0
  11. package/src/cli/actions/backup.js +93 -0
  12. package/src/cli/actions/gateway/start.js +17 -0
  13. package/src/cli/actions/get.js +34 -0
  14. package/src/cli/actions/keypair.js +59 -0
  15. package/src/cli/actions/login.js +110 -0
  16. package/src/cli/actions/logout.js +36 -0
  17. package/src/cli/actions/observe.js +36 -0
  18. package/src/cli/actions/open.js +37 -0
  19. package/src/cli/actions/rotate/github/connect.js +84 -0
  20. package/src/cli/actions/rotate/npm/connect.js +84 -0
  21. package/src/cli/actions/rotate/openai/connect.js +84 -0
  22. package/src/cli/actions/rotate.js +44 -0
  23. package/src/cli/actions/set.js +34 -0
  24. package/src/cli/actions/settings/device.js +24 -0
  25. package/src/cli/actions/settings/hostname.js +20 -0
  26. package/src/cli/actions/settings/off.js +17 -0
  27. package/src/cli/actions/settings/on.js +17 -0
  28. package/src/cli/actions/settings/path.js +21 -0
  29. package/src/cli/actions/settings/token.js +24 -0
  30. package/src/cli/actions/settings/username.js +22 -0
  31. package/src/cli/actions/status.js +13 -0
  32. package/src/cli/actions/sync.js +104 -0
  33. package/src/cli/commands/armor.js +73 -0
  34. package/src/cli/commands/gateway.js +16 -0
  35. package/src/cli/commands/rotate/github.js +26 -0
  36. package/src/cli/commands/rotate/npm.js +26 -0
  37. package/src/cli/commands/rotate/openai.js +26 -0
  38. package/src/cli/commands/rotate.js +32 -0
  39. package/src/cli/commands/settings.js +60 -0
  40. package/src/cli/dotenvx-ops.js +163 -0
  41. package/src/cli/postinstall.js +16 -0
  42. package/src/db/device.js +73 -0
  43. package/src/db/session.js +193 -0
  44. package/src/lib/api/getAccount.js +32 -0
  45. package/src/lib/api/getSynchronization.js +34 -0
  46. package/src/lib/api/getVersion.js +24 -0
  47. package/src/lib/api/postArmorDown.js +48 -0
  48. package/src/lib/api/postArmorMove.js +48 -0
  49. package/src/lib/api/postArmorPull.js +48 -0
  50. package/src/lib/api/postArmorPush.js +48 -0
  51. package/src/lib/api/postArmorUp.js +51 -0
  52. package/src/lib/api/postBackup.js +68 -0
  53. package/src/lib/api/postGet.js +37 -0
  54. package/src/lib/api/postKeypair.js +60 -0
  55. package/src/lib/api/postLogout.js +34 -0
  56. package/src/lib/api/postOauthDeviceCode.js +45 -0
  57. package/src/lib/api/postOauthToken.js +38 -0
  58. package/src/lib/api/postObserve.js +62 -0
  59. package/src/lib/api/postRotate.js +39 -0
  60. package/src/lib/api/postRotateConnect.js +54 -0
  61. package/src/lib/api/postSet.js +43 -0
  62. package/src/lib/api/postSync.js +68 -0
  63. package/src/lib/helpers/armoredKeyDisplay.js +10 -0
  64. package/src/lib/helpers/buildApiError.js +16 -0
  65. package/src/lib/helpers/buildOauthError.js +13 -0
  66. package/src/lib/helpers/canonicalEnvFilename.js +13 -0
  67. package/src/lib/helpers/clipboardy/fallbacks/linux/xsel +0 -0
  68. package/src/lib/helpers/clipboardy/fallbacks/windows/clipboard_i686.exe +0 -0
  69. package/src/lib/helpers/clipboardy/fallbacks/windows/clipboard_x86_64.exe +0 -0
  70. package/src/lib/helpers/clipboardy/linux.js +57 -0
  71. package/src/lib/helpers/clipboardy/macos.js +14 -0
  72. package/src/lib/helpers/clipboardy/termux.js +41 -0
  73. package/src/lib/helpers/clipboardy/windows.js +16 -0
  74. package/src/lib/helpers/clipboardy.js +51 -0
  75. package/src/lib/helpers/confirm.js +17 -0
  76. package/src/lib/helpers/createSpinner.js +101 -0
  77. package/src/lib/helpers/createSpinner2.js +24 -0
  78. package/src/lib/helpers/decryptValue.js +10 -0
  79. package/src/lib/helpers/dotenvxProjectId.js +36 -0
  80. package/src/lib/helpers/encryptValue.js +9 -0
  81. package/src/lib/helpers/errors.js +30 -0
  82. package/src/lib/helpers/formatCode.js +11 -0
  83. package/src/lib/helpers/gitBranch.js +13 -0
  84. package/src/lib/helpers/gitUrl.js +13 -0
  85. package/src/lib/helpers/http.js +17 -0
  86. package/src/lib/helpers/jsonToEnv.js +7 -0
  87. package/src/lib/helpers/keyNamesForEnvFile.js +43 -0
  88. package/src/lib/helpers/keypairMetadata.js +88 -0
  89. package/src/lib/helpers/likelyUpdateCommand.js +33 -0
  90. package/src/lib/helpers/localKeypair.js +12 -0
  91. package/src/lib/helpers/mask.js +12 -0
  92. package/src/lib/helpers/normalizePath.js +5 -0
  93. package/src/lib/helpers/normalizeToken.js +5 -0
  94. package/src/lib/helpers/openUrl.js +7 -0
  95. package/src/lib/helpers/packageJson.js +3 -0
  96. package/src/lib/helpers/playwrightConnect.js +29 -0
  97. package/src/lib/helpers/pluralize.js +10 -0
  98. package/src/lib/helpers/prompts.js +68 -0
  99. package/src/lib/helpers/removeEnvKey.js +50 -0
  100. package/src/lib/helpers/safeRealpath.js +13 -0
  101. package/src/lib/helpers/sha256File.js +9 -0
  102. package/src/lib/helpers/smartMask.js +11 -0
  103. package/src/lib/helpers/truncate.js +10 -0
  104. package/src/lib/helpers/upsertEnvKey.js +61 -0
  105. package/src/lib/main.d.ts +152 -0
  106. package/src/lib/main.js +114 -0
  107. package/src/lib/services/armorDown.js +77 -0
  108. package/src/lib/services/armorMove.js +54 -0
  109. package/src/lib/services/armorPull.js +77 -0
  110. package/src/lib/services/armorPush.js +82 -0
  111. package/src/lib/services/armorUp.js +79 -0
  112. package/src/lib/services/backup.js +105 -0
  113. package/src/lib/services/gatewayStart.js +132 -0
  114. package/src/lib/services/keypair.js +59 -0
  115. package/src/lib/services/loggedIn.js +24 -0
  116. package/src/lib/services/login.js +28 -0
  117. package/src/lib/services/loginPoll.js +43 -0
  118. package/src/lib/services/logout.js +28 -0
  119. package/src/lib/services/rotate.js +28 -0
  120. package/src/lib/services/rotateGithubConnect.js +56 -0
  121. package/src/lib/services/rotateNpmConnect.js +56 -0
  122. package/src/lib/services/rotateOpenaiConnect.js +60 -0
  123. package/src/lib/services/status.js +11 -0
  124. package/src/lib/services/sync.js +70 -0
  125. package/src/lib/services/syncConflict.js +36 -0
@@ -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,24 @@
1
+ const FRAMES = ['◇', '⬖', '◆', '⬗']
2
+ const FRAME_INTERVAL_MS = 80
3
+
4
+ async function createSpinner2 (options = {}) {
5
+ const stream = process.stderr
6
+ const hasCursorControls = typeof stream.cursorTo === 'function' && typeof stream.clearLine === 'function'
7
+ const enabled = Boolean(stream.isTTY && hasCursorControls && options.spinner !== false && !options.quiet && !options.verbose && !options.debug)
8
+ if (!enabled) return null
9
+
10
+ const text = options.text || 'thinking'
11
+ const frames = options.frames || FRAMES
12
+
13
+ const { default: yoctoSpinner } = await import('yocto-spinner')
14
+ return yoctoSpinner({
15
+ text,
16
+ spinner: {
17
+ frames,
18
+ interval: FRAME_INTERVAL_MS
19
+ },
20
+ stream
21
+ }).start()
22
+ }
23
+
24
+ module.exports = createSpinner2
@@ -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,36 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const dotenvx = require('@dotenvx/dotenvx')
4
+ const Errors = require('./errors')
5
+
6
+ function dotenvxProjectId (cwd = process.cwd(), raiseErrors = true) {
7
+ // 1. Prefer environment variable
8
+ if (process.env.DOTENVX_PROJECT_ID && process.env.DOTENVX_PROJECT_ID.trim()) {
9
+ return process.env.DOTENVX_PROJECT_ID.trim()
10
+ }
11
+
12
+ // 2. Otherwise, parse .env.x contents
13
+ const filepath = path.join(cwd, '.env.x')
14
+ // file must exist
15
+ if (!fs.existsSync(filepath)) {
16
+ const filename = path.basename(filepath)
17
+ if (raiseErrors) {
18
+ throw new Errors({ filename, filepath }).envXFileMissing()
19
+ } else {
20
+ return null
21
+ }
22
+ }
23
+
24
+ if (fs.existsSync(filepath)) {
25
+ const src = fs.readFileSync(filepath, 'utf8')
26
+ const parsed = dotenvx.parse(src)
27
+ if (parsed.DOTENVX_PROJECT_ID && parsed.DOTENVX_PROJECT_ID.trim()) {
28
+ return parsed.DOTENVX_PROJECT_ID.trim()
29
+ }
30
+ }
31
+
32
+ // 3. Nothing found
33
+ return null
34
+ }
35
+
36
+ module.exports = dotenvxProjectId
@@ -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,43 @@
1
+ const canonicalEnvFilename = require('./canonicalEnvFilename')
2
+
3
+ function environment (filepath) {
4
+ const filename = canonicalEnvFilename(filepath)
5
+
6
+ const parts = filename.split('.')
7
+ const possibleEnvironmentList = [...parts.slice(2)]
8
+
9
+ if (possibleEnvironmentList.length === 0) {
10
+ // handle .env1 -> development1
11
+ return filename.replace('.env', 'development')
12
+ }
13
+
14
+ if (possibleEnvironmentList.length === 1) {
15
+ return possibleEnvironmentList[0]
16
+ }
17
+
18
+ if (possibleEnvironmentList.length === 2) {
19
+ return possibleEnvironmentList.join('_')
20
+ }
21
+
22
+ return possibleEnvironmentList.slice(0, 2).join('_')
23
+ }
24
+
25
+ function keyNamesForEnvFile (filepath = '.env') {
26
+ const filename = canonicalEnvFilename(filepath)
27
+
28
+ if (filename === '.env') {
29
+ return {
30
+ publicKeyName: 'DOTENV_PUBLIC_KEY',
31
+ privateKeyName: 'DOTENV_PRIVATE_KEY'
32
+ }
33
+ }
34
+
35
+ const resolvedEnvironment = environment(filename).toUpperCase()
36
+
37
+ return {
38
+ publicKeyName: `DOTENV_PUBLIC_KEY_${resolvedEnvironment}`,
39
+ privateKeyName: `DOTENV_PRIVATE_KEY_${resolvedEnvironment}`
40
+ }
41
+ }
42
+
43
+ module.exports = keyNamesForEnvFile
@@ -0,0 +1,88 @@
1
+ const path = require('path')
2
+ const fs = require('fs')
3
+ const execa = require('execa')
4
+ const dotenvx = require('@dotenvx/dotenvx')
5
+
6
+ const canonicalEnvFilename = require('./canonicalEnvFilename')
7
+
8
+ function compact (object) {
9
+ return Object.entries(object).reduce((acc, [key, value]) => {
10
+ if (value !== undefined && value !== null && value !== '') {
11
+ acc[key] = value
12
+ }
13
+
14
+ return acc
15
+ }, {})
16
+ }
17
+
18
+ function gitRoot () {
19
+ try {
20
+ return execa.sync('git', ['rev-parse', '--show-toplevel']).stdout.toString().trim()
21
+ } catch (_error) {
22
+ return null
23
+ }
24
+ }
25
+
26
+ function normalizeFilepath (envFile) {
27
+ const root = gitRoot()
28
+ const filepath = path.resolve(envFile)
29
+
30
+ if (root) {
31
+ return path.relative(root, filepath).replace(/\\/g, '/')
32
+ }
33
+
34
+ return path.relative(process.cwd(), filepath).replace(/\\/g, '/')
35
+ }
36
+
37
+ function environment (envFile) {
38
+ const filename = canonicalEnvFilename(envFile)
39
+ const parts = filename.split('.')
40
+ const possibleEnvironmentList = [...parts.slice(2)]
41
+
42
+ if (possibleEnvironmentList.length === 0) {
43
+ return filename.replace('.env', 'development')
44
+ }
45
+
46
+ if (possibleEnvironmentList.length === 1) {
47
+ return possibleEnvironmentList[0]
48
+ }
49
+
50
+ if (possibleEnvironmentList.length === 2) {
51
+ return possibleEnvironmentList.join('_')
52
+ }
53
+
54
+ return possibleEnvironmentList.slice(0, 2).join('_')
55
+ }
56
+
57
+ function projectName () {
58
+ return path.basename(gitRoot() || process.cwd())
59
+ }
60
+
61
+ function envKeys (envFile) {
62
+ let src
63
+ try {
64
+ src = fs.readFileSync(envFile)
65
+ } catch (_error) {
66
+ return null
67
+ }
68
+
69
+ const parsed = dotenvx.parse(src, {
70
+ ignore: ['MISSING_PRIVATE_KEY', 'WRONG_PRIVATE_KEY', 'INVALID_PRIVATE_KEY']
71
+ })
72
+
73
+ if (!parsed || Object.keys(parsed).length === 0) return null
74
+
75
+ return Object.keys(parsed)
76
+ }
77
+
78
+ function keypairMetadata (envFile = '.env') {
79
+ return compact({
80
+ filepath: normalizeFilepath(envFile),
81
+ filename: canonicalEnvFilename(envFile),
82
+ environment: environment(envFile),
83
+ project_name: projectName(),
84
+ keys: envKeys(envFile)
85
+ })
86
+ }
87
+
88
+ module.exports = keypairMetadata
@@ -0,0 +1,33 @@
1
+ const normalizePath = require('./normalizePath')
2
+ const safeRealpath = require('./safeRealpath')
3
+
4
+ const NPM_COMMAND = 'npm i @dotenvx/dotenvx-ops'
5
+ const CURL_COMMAND = 'curl -sfS https://dotenvx.sh/ops | sh'
6
+
7
+ function likelyUpdateCommand () {
8
+ const isPackaged = Boolean(process.pkg)
9
+ const executablePath = isPackaged ? process.execPath : (process.argv[1] || process.execPath)
10
+ const resolvedExecutablePath = safeRealpath(executablePath)
11
+ const normalizedPath = normalizePath(resolvedExecutablePath || executablePath || '')
12
+
13
+ if (
14
+ normalizedPath.includes('/node_modules/@dotenvx/dotenvx-ops/') ||
15
+ normalizedPath.includes('/node_modules/.bin/dotenvx-ops')
16
+ ) {
17
+ return NPM_COMMAND
18
+ }
19
+
20
+ if (
21
+ isPackaged ||
22
+ normalizedPath.endsWith('/usr/local/bin/dotenvx-ops') ||
23
+ normalizedPath.endsWith('/opt/homebrew/bin/dotenvx-ops') ||
24
+ normalizedPath.endsWith('/usr/bin/dotenvx-ops') ||
25
+ normalizedPath.endsWith('/bin/dotenvx-ops')
26
+ ) {
27
+ return CURL_COMMAND
28
+ }
29
+
30
+ return NPM_COMMAND
31
+ }
32
+
33
+ module.exports = likelyUpdateCommand
@@ -0,0 +1,12 @@
1
+ const { PrivateKey } = require('eciesjs')
2
+
3
+ function localKeypair () {
4
+ const kp = new PrivateKey()
5
+
6
+ return {
7
+ public_key: kp.publicKey.toHex(),
8
+ private_key: kp.secret.toString('hex')
9
+ }
10
+ }
11
+
12
+ module.exports = localKeypair
@@ -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,5 @@
1
+ function normalizePath (value) {
2
+ return String(value || '').replace(/\\/g, '/').toLowerCase()
3
+ }
4
+
5
+ module.exports = normalizePath
@@ -0,0 +1,5 @@
1
+ function normalizeToken (token) {
2
+ return token == null ? '' : token
3
+ }
4
+
5
+ module.exports = normalizeToken
@@ -0,0 +1,7 @@
1
+ const open = require('open')
2
+
3
+ async function openUrl (url) {
4
+ return open(url, { wait: true })
5
+ }
6
+
7
+ module.exports = openUrl
@@ -0,0 +1,3 @@
1
+ const { name, version, description } = require('../../../package.json')
2
+
3
+ module.exports = { name, version, description }
@@ -0,0 +1,29 @@
1
+ const { logger } = require('@dotenvx/dotenvx')
2
+ const { chromium } = require('playwright')
3
+
4
+ const VIEWPORT_WIDTH = 1200
5
+ const VIEWPORT_HEIGHT = 742
6
+ const VIEWPORT = { width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT }
7
+
8
+ async function playwrightConnect (channel = 'chrome') {
9
+ const browser = await ensurePlaywrightBrowser(channel)
10
+ const context = await browser.newContext({ viewport: VIEWPORT })
11
+ const page = await context.newPage()
12
+
13
+ return { browser, context, page }
14
+ }
15
+
16
+ async function ensurePlaywrightBrowser (channel = 'chrome') {
17
+ try {
18
+ return await chromium.launch({ headless: false, channel })
19
+ } catch (err) {
20
+ if (String(err).includes('Executable doesn\'t exist')) {
21
+ logger.info('Installing chromium...')
22
+ const cp = require('child_process')
23
+ cp.execSync('npx playwright install chromium', { stdio: 'inherit' })
24
+ return await chromium.launch({ headless: false })
25
+ }
26
+ }
27
+ }
28
+
29
+ module.exports = playwrightConnect
@@ -0,0 +1,10 @@
1
+ function pluralize (word, count) {
2
+ // simple pluralization: add 's' at the end
3
+ if (count === 0 || count > 1) {
4
+ return word + 's'
5
+ } else {
6
+ return word
7
+ }
8
+ }
9
+
10
+ module.exports = pluralize
@@ -0,0 +1,68 @@
1
+ const Enquirer = require('enquirer')
2
+
3
+ const enquirer = new Enquirer()
4
+
5
+ function choicesForSelect (choices) {
6
+ return choices.map(choice => {
7
+ if (typeof choice === 'string') return choice
8
+
9
+ return {
10
+ name: choice.value,
11
+ message: choice.name || choice.value
12
+ }
13
+ })
14
+ }
15
+
16
+ function enquirerOptions (context = {}) {
17
+ const options = {}
18
+
19
+ if (context.input) {
20
+ options.stdin = context.input
21
+ }
22
+
23
+ if (context.output) {
24
+ options.stdout = context.output
25
+ }
26
+
27
+ return options
28
+ }
29
+
30
+ async function select ({ message, choices }, context) {
31
+ const answer = await enquirer.prompt({
32
+ type: 'select',
33
+ name: 'value',
34
+ message,
35
+ choices: choicesForSelect(choices),
36
+ ...enquirerOptions(context)
37
+ })
38
+
39
+ return answer.value
40
+ }
41
+
42
+ async function input ({ message }, context) {
43
+ const answer = await enquirer.prompt({
44
+ type: 'input',
45
+ name: 'value',
46
+ message,
47
+ ...enquirerOptions(context)
48
+ })
49
+
50
+ return answer.value
51
+ }
52
+
53
+ async function password ({ message }, context) {
54
+ const answer = await enquirer.prompt({
55
+ type: 'password',
56
+ name: 'value',
57
+ message,
58
+ ...enquirerOptions(context)
59
+ })
60
+
61
+ return answer.value
62
+ }
63
+
64
+ module.exports = {
65
+ select,
66
+ input,
67
+ password
68
+ }
@@ -0,0 +1,50 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+
4
+ function escapeForRegex (value) {
5
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
6
+ }
7
+
8
+ function removeEnvKey (key, keysFilepath = '.env.keys') {
9
+ const resolvedKeysFilepath = path.resolve(keysFilepath)
10
+
11
+ if (!fs.existsSync(resolvedKeysFilepath)) {
12
+ return {
13
+ changed: false,
14
+ key,
15
+ filepath: keysFilepath
16
+ }
17
+ }
18
+
19
+ const src = fs.readFileSync(resolvedKeysFilepath, 'utf8')
20
+ const eol = src.includes('\r\n') ? '\r\n' : '\n'
21
+ const keyPattern = new RegExp(`^\\s*(?:export\\s+)?${escapeForRegex(key)}\\s*=`)
22
+ const envKeyPattern = /^\s*(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*\s*=/
23
+ const lines = src.length > 0 ? src.split(/\r?\n/) : []
24
+
25
+ while (lines.length > 0 && lines[lines.length - 1] === '') {
26
+ lines.pop()
27
+ }
28
+
29
+ const nextLines = lines.filter((line) => !keyPattern.test(line))
30
+ const changed = nextLines.length !== lines.length
31
+
32
+ if (changed) {
33
+ const hasRemainingKeys = nextLines.some((line) => envKeyPattern.test(line))
34
+
35
+ if (hasRemainingKeys) {
36
+ const nextSrc = `${nextLines.join(eol)}${eol}`
37
+ fs.writeFileSync(resolvedKeysFilepath, nextSrc, 'utf8')
38
+ } else {
39
+ fs.rmSync(resolvedKeysFilepath, { force: true })
40
+ }
41
+ }
42
+
43
+ return {
44
+ changed,
45
+ key,
46
+ filepath: keysFilepath
47
+ }
48
+ }
49
+
50
+ module.exports = removeEnvKey
@@ -0,0 +1,13 @@
1
+ const fs = require('fs')
2
+
3
+ function safeRealpath (filePath) {
4
+ if (!filePath) return filePath
5
+
6
+ try {
7
+ return fs.realpathSync(filePath)
8
+ } catch (e) {
9
+ return filePath
10
+ }
11
+ }
12
+
13
+ module.exports = safeRealpath