@heroku/heroku-cli-util 8.0.15 → 9.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -3
- package/README.md +44 -241
- package/dist/test-helpers/expect-output.d.ts +2 -0
- package/dist/test-helpers/expect-output.js +16 -0
- package/dist/test-helpers/init.d.ts +1 -0
- package/dist/test-helpers/init.js +18 -0
- package/dist/test-helpers/stub-output.d.ts +2 -0
- package/dist/test-helpers/stub-output.js +33 -0
- package/dist/types/errors/ambiguous.d.ts +15 -0
- package/dist/types/errors/ambiguous.js +14 -0
- package/dist/types/errors/not-found.d.ts +5 -0
- package/dist/types/errors/not-found.js +12 -0
- package/dist/types/pg/data-api.d.ts +17 -0
- package/dist/types/pg/data-api.js +2 -0
- package/dist/types/pg/tunnel.d.ts +22 -0
- package/dist/types/pg/tunnel.js +2 -0
- package/dist/utils/addons/resolve.d.ts +9 -0
- package/dist/utils/addons/resolve.js +39 -0
- package/dist/utils/pg/bastion.d.ts +30 -0
- package/dist/utils/pg/bastion.js +122 -0
- package/dist/utils/pg/config-vars.d.ts +8 -0
- package/dist/utils/pg/config-vars.js +34 -0
- package/dist/utils/pg/databases.d.ts +12 -0
- package/dist/utils/pg/databases.js +137 -0
- package/dist/utils/pg/host.d.ts +1 -0
- package/dist/utils/pg/host.js +7 -0
- package/dist/utils/pg/psql.d.ts +28 -0
- package/dist/utils/pg/psql.js +188 -0
- package/dist/ux/confirm.d.ts +1 -0
- package/dist/ux/confirm.js +7 -0
- package/dist/ux/prompt.d.ts +2 -0
- package/dist/ux/prompt.js +7 -0
- package/dist/ux/styled-header.d.ts +1 -0
- package/dist/ux/styled-header.js +7 -0
- package/dist/ux/styled-json.d.ts +1 -0
- package/dist/ux/styled-json.js +7 -0
- package/dist/ux/styled-object.d.ts +1 -0
- package/dist/ux/styled-object.js +7 -0
- package/dist/ux/table.d.ts +2 -0
- package/dist/ux/table.js +7 -0
- package/dist/ux/wait.d.ts +1 -0
- package/dist/ux/wait.js +7 -0
- package/package.json +54 -55
- package/index.js +0 -40
- package/lib/action.js +0 -54
- package/lib/auth.js +0 -207
- package/lib/command.js +0 -171
- package/lib/console.js +0 -105
- package/lib/date.js +0 -18
- package/lib/errors.js +0 -122
- package/lib/exit.js +0 -42
- package/lib/got.js +0 -153
- package/lib/linewrap.js +0 -783
- package/lib/mutex.js +0 -41
- package/lib/open.js +0 -22
- package/lib/preauth.js +0 -26
- package/lib/process.js +0 -14
- package/lib/prompt.js +0 -150
- package/lib/spinner.js +0 -147
- package/lib/spinners.json +0 -739
- package/lib/styled.js +0 -131
- package/lib/table.js +0 -132
- package/lib/util.js +0 -38
- package/lib/vars.js +0 -29
- package/lib/yubikey.js +0 -14
package/lib/mutex.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
// Adapted from https://blog.jcoglan.com/2016/07/12/mutexes-and-javascript/
|
|
4
|
-
|
|
5
|
-
let Mutex = function () {
|
|
6
|
-
this._busy = false
|
|
7
|
-
this._queue = []
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
Mutex.prototype.synchronize = function (task) {
|
|
11
|
-
let self = this
|
|
12
|
-
|
|
13
|
-
return new Promise(function (resolve, reject) {
|
|
14
|
-
self._queue.push([task, resolve, reject])
|
|
15
|
-
if (!self._busy) {
|
|
16
|
-
self._dequeue()
|
|
17
|
-
}
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
Mutex.prototype._dequeue = function () {
|
|
22
|
-
this._busy = true
|
|
23
|
-
let next = this._queue.shift()
|
|
24
|
-
|
|
25
|
-
if (next) {
|
|
26
|
-
this._execute(next)
|
|
27
|
-
} else {
|
|
28
|
-
this._busy = false
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
Mutex.prototype._execute = function (record) {
|
|
33
|
-
let [task, resolve, reject] = record
|
|
34
|
-
let self = this
|
|
35
|
-
|
|
36
|
-
task().then(resolve, reject).then(function () {
|
|
37
|
-
self._dequeue()
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
module.exports = Mutex
|
package/lib/open.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
let { color } = require('..')
|
|
4
|
-
|
|
5
|
-
function open (url, browser) {
|
|
6
|
-
let opn = require('opn')
|
|
7
|
-
return new Promise((resolve, reject) => {
|
|
8
|
-
let opts = { wait: false }
|
|
9
|
-
if (browser) { opts.app = browser }
|
|
10
|
-
opn(url, opts, err => {
|
|
11
|
-
if (err) {
|
|
12
|
-
reject(new Error(
|
|
13
|
-
`Error opening web browser.
|
|
14
|
-
${err}
|
|
15
|
-
|
|
16
|
-
Manually visit ${color.cyan(url)} in your browser.`))
|
|
17
|
-
} else resolve(err)
|
|
18
|
-
})
|
|
19
|
-
})
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
module.exports = open
|
package/lib/preauth.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
let util = require('./util')
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* preauth will make an API call to preauth a user for an app
|
|
6
|
-
* this makes it so the user will not have to enter a 2fa code
|
|
7
|
-
* for the next few minutes on the specified app.
|
|
8
|
-
*
|
|
9
|
-
* You need this if your command is going to make multiple API calls
|
|
10
|
-
* since otherwise the secondFactor key would only work one time for
|
|
11
|
-
* yubikeys.
|
|
12
|
-
*
|
|
13
|
-
* @param {String} app the app to preauth against
|
|
14
|
-
* @param {Heroku} heroku a heroku api client
|
|
15
|
-
* @param {String} secondFactor a second factor code
|
|
16
|
-
* @return {Promise} A promise fulfilled when the preauth is complete
|
|
17
|
-
*/
|
|
18
|
-
function preauth (app, heroku, secondFactor) {
|
|
19
|
-
return heroku.request({
|
|
20
|
-
method: 'PUT',
|
|
21
|
-
path: `/apps/${app}/pre-authorizations`,
|
|
22
|
-
headers: { 'Heroku-Two-Factor-Code': secondFactor }
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
module.exports = util.promiseOrCallback(preauth)
|
package/lib/process.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
function Process () {
|
|
2
|
-
this.env = process.env
|
|
3
|
-
}
|
|
4
|
-
Process.prototype.mock = function () {
|
|
5
|
-
this.mocking = true
|
|
6
|
-
}
|
|
7
|
-
Process.prototype.exit = function (code) {
|
|
8
|
-
if (this.mocking) {
|
|
9
|
-
this.exitCode = code
|
|
10
|
-
} else {
|
|
11
|
-
process.exit(code)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
module.exports = new Process()
|
package/lib/prompt.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const cli = require('../')
|
|
4
|
-
const errors = require('./errors')
|
|
5
|
-
const util = require('./util')
|
|
6
|
-
const color = cli.color
|
|
7
|
-
const ansi = require('ansi-escapes')
|
|
8
|
-
const Spinner = require('./spinner')
|
|
9
|
-
const nodeUtil = require('util')
|
|
10
|
-
|
|
11
|
-
function promptMasked (options) {
|
|
12
|
-
return new Promise(function (resolve, reject) {
|
|
13
|
-
let stdin = process.stdin
|
|
14
|
-
let stderr = process.stderr
|
|
15
|
-
let input = ''
|
|
16
|
-
stdin.setEncoding('utf8')
|
|
17
|
-
stderr.write(ansi.eraseLine)
|
|
18
|
-
stderr.write(ansi.cursorLeft)
|
|
19
|
-
cli.console.writeError(options.prompt)
|
|
20
|
-
stdin.resume()
|
|
21
|
-
stdin.setRawMode(true)
|
|
22
|
-
|
|
23
|
-
function stop () {
|
|
24
|
-
if (!options.hide) {
|
|
25
|
-
stderr.write(
|
|
26
|
-
ansi.cursorHide +
|
|
27
|
-
ansi.cursorLeft +
|
|
28
|
-
options.prompt +
|
|
29
|
-
input.replace(/./g, '*') +
|
|
30
|
-
'\n' +
|
|
31
|
-
ansi.cursorShow)
|
|
32
|
-
} else {
|
|
33
|
-
stderr.write('\n')
|
|
34
|
-
}
|
|
35
|
-
stdin.removeListener('data', fn)
|
|
36
|
-
stdin.setRawMode(false)
|
|
37
|
-
stdin.pause()
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function enter () {
|
|
41
|
-
if (input.length === 0) return
|
|
42
|
-
stop()
|
|
43
|
-
resolve(input)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function ctrlc () {
|
|
47
|
-
reject(new Error(''))
|
|
48
|
-
stop()
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function backspace () {
|
|
52
|
-
if (input.length === 0) return
|
|
53
|
-
input = input.substr(0, input.length - 1)
|
|
54
|
-
stderr.write(ansi.cursorBackward(1))
|
|
55
|
-
stderr.write(ansi.eraseEndLine)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function newchar (c) {
|
|
59
|
-
input += c
|
|
60
|
-
stderr.write(options.hide ? '*'.repeat(c.length) : c)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
let fn = function (c) {
|
|
64
|
-
switch (c) {
|
|
65
|
-
case '\u0004': // Ctrl-d
|
|
66
|
-
case '\r':
|
|
67
|
-
case '\n':
|
|
68
|
-
return enter()
|
|
69
|
-
case '\u0003': // Ctrl-c
|
|
70
|
-
return ctrlc()
|
|
71
|
-
default:
|
|
72
|
-
// backspace
|
|
73
|
-
if (c.charCodeAt(0) === 127) return backspace()
|
|
74
|
-
else return newchar(c)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
stdin.on('data', fn)
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function PromptMaskError (message) {
|
|
82
|
-
Error.call(this)
|
|
83
|
-
Error.captureStackTrace(this, this.constructor)
|
|
84
|
-
this.name = this.constructor.name
|
|
85
|
-
this.message = message
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
nodeUtil.inherits(PromptMaskError, Error)
|
|
89
|
-
|
|
90
|
-
exports.PromptMaskError = PromptMaskError
|
|
91
|
-
|
|
92
|
-
function prompt (name, options) {
|
|
93
|
-
options = options || {}
|
|
94
|
-
options.name = name
|
|
95
|
-
options.prompt = name ? color.dim(`${name}: `) : color.dim('> ')
|
|
96
|
-
let isTTY = process.env.TERM !== 'dumb' && process.stdin.isTTY
|
|
97
|
-
let spinnerTask
|
|
98
|
-
if (options.mask || options.hide) {
|
|
99
|
-
if (!isTTY) {
|
|
100
|
-
return Promise.reject(new PromptMaskError(`CLI needs to prompt for ${options.name || options.prompt} but stdin is not a tty.`))
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
spinnerTask = function () {
|
|
104
|
-
return promptMasked(options)
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
spinnerTask = function () {
|
|
108
|
-
return new Promise(function (resolve) {
|
|
109
|
-
process.stdin.setEncoding('utf8')
|
|
110
|
-
cli.console.writeError(options.prompt)
|
|
111
|
-
process.stdin.resume()
|
|
112
|
-
process.stdin.once('data', function (data) {
|
|
113
|
-
process.stdin.pause()
|
|
114
|
-
data = data.trim()
|
|
115
|
-
if (data === '') {
|
|
116
|
-
resolve(prompt(name))
|
|
117
|
-
} else {
|
|
118
|
-
resolve(data)
|
|
119
|
-
}
|
|
120
|
-
})
|
|
121
|
-
})
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return Spinner.prompt(spinnerTask)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function confirmApp (app, confirm, message) {
|
|
128
|
-
return new Promise(function (resolve, reject) {
|
|
129
|
-
if (confirm) {
|
|
130
|
-
if (confirm === app) return resolve()
|
|
131
|
-
return reject(new Error(`Confirmation ${cli.color.bold.red(confirm)} did not match ${cli.color.bold.red(app)}. Aborted.`))
|
|
132
|
-
}
|
|
133
|
-
if (!message) {
|
|
134
|
-
message = `WARNING: Destructive Action
|
|
135
|
-
This command will affect the app ${cli.color.bold.red(app)}`
|
|
136
|
-
}
|
|
137
|
-
errors.warn(message)
|
|
138
|
-
errors.warn(`To proceed, type ${cli.color.bold.red(app)} or re-run this command with ${cli.color.bold.red('--confirm', app)}`)
|
|
139
|
-
console.error()
|
|
140
|
-
prompt().then(function (confirm) {
|
|
141
|
-
if (confirm === app) {
|
|
142
|
-
return resolve()
|
|
143
|
-
}
|
|
144
|
-
return reject(new Error(`Confirmation did not match ${cli.color.bold.red(app)}. Aborted.`))
|
|
145
|
-
})
|
|
146
|
-
})
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
exports.prompt = util.promiseOrCallback(prompt)
|
|
150
|
-
exports.confirmApp = util.promiseOrCallback(confirmApp)
|
package/lib/spinner.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
// code mostly from https://github.com/sindresorhus/ora
|
|
4
|
-
|
|
5
|
-
const stripAnsi = require('strip-ansi')
|
|
6
|
-
let console = require('./console')
|
|
7
|
-
let color = require('@heroku-cli/color').default
|
|
8
|
-
let errors = require('./errors')
|
|
9
|
-
|
|
10
|
-
class Spinner {
|
|
11
|
-
constructor (options) {
|
|
12
|
-
this.options = Object.assign({
|
|
13
|
-
text: ''
|
|
14
|
-
}, options)
|
|
15
|
-
|
|
16
|
-
this.ansi = require('ansi-escapes')
|
|
17
|
-
let spinners = require('./spinners.json')
|
|
18
|
-
|
|
19
|
-
this.color = this.options.color || 'heroku'
|
|
20
|
-
this.spinner = process.platform === 'win32' ? spinners.line : (this.options.spinner ? spinners[this.options.spinner] : spinners.dots2)
|
|
21
|
-
this.text = this.options.text
|
|
22
|
-
this.interval = this.options.interval || this.spinner.interval || 100
|
|
23
|
-
this.id = null
|
|
24
|
-
this.frameIndex = 0
|
|
25
|
-
this.stream = this.options.stream || process.stderr
|
|
26
|
-
this.enabled = !console.mocking() && (this.stream && this.stream.isTTY) && !process.env.CI && !['dumb', 'emacs-color'].includes(process.env.TERM)
|
|
27
|
-
this.warnings = []
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
start () {
|
|
31
|
-
if (this.id) return
|
|
32
|
-
if (!this.enabled) {
|
|
33
|
-
console.writeError(this.text)
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
this.stream.write(this.ansi.cursorLeft)
|
|
38
|
-
this.stream.write(this.ansi.eraseLine)
|
|
39
|
-
this.stream.write(this.ansi.cursorHide)
|
|
40
|
-
this._render()
|
|
41
|
-
this.id = setInterval(this._spin.bind(this), this.interval)
|
|
42
|
-
process.on('SIGWINCH', this._sigwinch = this._render.bind(this))
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
stop (status) {
|
|
46
|
-
if (status && !this.enabled) console.error(` ${status}`)
|
|
47
|
-
if (!this.enabled) return
|
|
48
|
-
if (status) this._status = status
|
|
49
|
-
|
|
50
|
-
process.removeListener('SIGWINCH', this._sigwinch)
|
|
51
|
-
clearInterval(this.id)
|
|
52
|
-
this.id = null
|
|
53
|
-
this.enabled = false
|
|
54
|
-
this.frameIndex = 0
|
|
55
|
-
this._render()
|
|
56
|
-
this.stream.write(this.ansi.cursorShow)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
warn (msg) {
|
|
60
|
-
if (!this.enabled) {
|
|
61
|
-
console.writeError(color.yellow(' !') + '\n' + errors.renderWarning(msg) + '\n' + this.text)
|
|
62
|
-
} else {
|
|
63
|
-
this.warnings.push(msg)
|
|
64
|
-
this._render()
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
get status () {
|
|
69
|
-
return this._status
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
set status (status) {
|
|
73
|
-
this._status = status
|
|
74
|
-
if (this.enabled) this._render()
|
|
75
|
-
else console.writeError(` ${this.status}\n${this.text}`)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
clear () {
|
|
79
|
-
if (!this._output) return
|
|
80
|
-
this.stream.write(this.ansi.cursorUp(this._lines(this._output)))
|
|
81
|
-
this.stream.write(this.ansi.eraseDown)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
_render () {
|
|
85
|
-
if (this._output) this.clear()
|
|
86
|
-
this._output = `${this.text}${this.enabled ? ' ' + this._frame() : ''} ${this.status ? this.status : ''}\n` +
|
|
87
|
-
this.warnings.map(w => errors.renderWarning(w) + '\n').join('')
|
|
88
|
-
this.stream.write(this._output)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
_lines (s) {
|
|
92
|
-
return stripAnsi(s)
|
|
93
|
-
.split('\n')
|
|
94
|
-
.map(l => Math.ceil(l.length / this._width))
|
|
95
|
-
.reduce((c, i) => c + i, 0)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
get _width () {
|
|
99
|
-
return errors.errtermwidth()
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
_spin () {
|
|
103
|
-
if (Spinner.prompts.length > 0) {
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
this.stream.write(this.ansi.cursorUp(this._lines(this._output)))
|
|
108
|
-
let y = this._lines(this.text) - 1
|
|
109
|
-
let lastline = stripAnsi(this.text).split('\n').pop()
|
|
110
|
-
let x = 1 + lastline.length - (this._lines(lastline) - 1) * this._width
|
|
111
|
-
this.stream.write(this.ansi.cursorMove(x, y))
|
|
112
|
-
this.stream.write(this._frame())
|
|
113
|
-
this.stream.write(this.ansi.cursorDown(this._lines(this._output) - y))
|
|
114
|
-
this.stream.write(this.ansi.cursorLeft)
|
|
115
|
-
this.stream.write(this.ansi.eraseLine)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
_frame () {
|
|
119
|
-
var frames = this.spinner.frames
|
|
120
|
-
var frame = frames[this.frameIndex]
|
|
121
|
-
if (this.color) frame = color[this.color](frame)
|
|
122
|
-
this.frameIndex = ++this.frameIndex % frames.length
|
|
123
|
-
return frame
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
static prompt (promptFn) {
|
|
127
|
-
let removeFn = function () {
|
|
128
|
-
Spinner.prompts = Spinner.prompts.filter(p => p !== promptFn)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
Spinner.prompts.push(promptFn)
|
|
132
|
-
|
|
133
|
-
return promptFn()
|
|
134
|
-
.then(data => {
|
|
135
|
-
removeFn()
|
|
136
|
-
return data
|
|
137
|
-
})
|
|
138
|
-
.catch(err => {
|
|
139
|
-
removeFn()
|
|
140
|
-
throw err
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
Spinner.prompts = []
|
|
146
|
-
|
|
147
|
-
module.exports = Spinner
|