@heroku/heroku-cli-util 8.0.12
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 +3 -0
- package/README.md +269 -0
- package/index.js +40 -0
- package/lib/action.js +54 -0
- package/lib/auth.js +207 -0
- package/lib/command.js +171 -0
- package/lib/console.js +105 -0
- package/lib/date.js +18 -0
- package/lib/errors.js +122 -0
- package/lib/exit.js +42 -0
- package/lib/got.js +153 -0
- package/lib/linewrap.js +783 -0
- package/lib/mutex.js +41 -0
- package/lib/open.js +22 -0
- package/lib/preauth.js +26 -0
- package/lib/process.js +14 -0
- package/lib/prompt.js +150 -0
- package/lib/spinner.js +147 -0
- package/lib/spinners.json +739 -0
- package/lib/styled.js +131 -0
- package/lib/table.js +132 -0
- package/lib/util.js +38 -0
- package/lib/vars.js +29 -0
- package/lib/yubikey.js +14 -0
- package/package.json +80 -0
package/lib/styled.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
let cli = require('..')
|
|
4
|
+
let util = require('util')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* styledJSON prints out colored, indented JSON
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* styledHeader({foo: 'bar'}) # Outputs === {
|
|
11
|
+
* "foo": "bar"
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* @param {obj} object data to display
|
|
15
|
+
* @returns {null}
|
|
16
|
+
*/
|
|
17
|
+
function styledJSON (obj) {
|
|
18
|
+
let json = JSON.stringify(obj, null, 2)
|
|
19
|
+
if (cli.color.enabled) {
|
|
20
|
+
let cardinal = require('cardinal')
|
|
21
|
+
let theme = require('cardinal/themes/jq')
|
|
22
|
+
cli.log(cardinal.highlight(json, { json: true, theme: theme }))
|
|
23
|
+
} else {
|
|
24
|
+
cli.log(json)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* styledHeader logs in a consistent header style
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* styledHeader('MyApp') # Outputs === MyApp
|
|
33
|
+
*
|
|
34
|
+
* @param {header} header text
|
|
35
|
+
* @returns {null}
|
|
36
|
+
*/
|
|
37
|
+
function styledHeader (header) {
|
|
38
|
+
cli.log(cli.color.dim('=== ') + cli.color.bold(header))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* styledObject logs an object in a consistent columnar style
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* styledObject({name: "myapp", collaborators: ["user1@example.com", "user2@example.com"]})
|
|
46
|
+
* Collaborators: user1@example.com
|
|
47
|
+
* user2@example.com
|
|
48
|
+
* Name: myapp
|
|
49
|
+
*
|
|
50
|
+
* @param {obj} object data to print
|
|
51
|
+
* @param {keys} optional array of keys to sort/filter output
|
|
52
|
+
* @returns {null}
|
|
53
|
+
*/
|
|
54
|
+
function styledObject (obj, keys) {
|
|
55
|
+
let keyLengths = Object.keys(obj).map(key => key.toString().length)
|
|
56
|
+
let maxKeyLength = Math.max.apply(Math, keyLengths) + 2
|
|
57
|
+
function pp (obj) {
|
|
58
|
+
if (typeof obj === 'string' || typeof obj === 'number') {
|
|
59
|
+
return obj
|
|
60
|
+
} else if (typeof obj === 'object') {
|
|
61
|
+
return Object.keys(obj).map(k => k + ': ' + util.inspect(obj[k])).join(', ')
|
|
62
|
+
} else {
|
|
63
|
+
return util.inspect(obj)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function logKeyValue (key, value) {
|
|
67
|
+
cli.log(`${key}:` + ' '.repeat(maxKeyLength - key.length - 1) + pp(value))
|
|
68
|
+
}
|
|
69
|
+
for (var key of (keys || Object.keys(obj).sort())) {
|
|
70
|
+
let value = obj[key]
|
|
71
|
+
if (Array.isArray(value)) {
|
|
72
|
+
if (value.length > 0) {
|
|
73
|
+
logKeyValue(key, value[0])
|
|
74
|
+
for (var e of value.slice(1)) {
|
|
75
|
+
cli.log(' '.repeat(maxKeyLength) + pp(e))
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} else if (value !== null && value !== undefined) {
|
|
79
|
+
logKeyValue(key, value)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* styledNameValues logs an array of {name: '', values: ['']} objects in a consistent style
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* styledNameValues([{name: "Collaborators", values: ["user1@example.com", "user2@example.com"]},
|
|
89
|
+
* {name: "Name", values: ["myapp"]}])
|
|
90
|
+
* Collaborators: user1@example.com
|
|
91
|
+
* user2@example.com
|
|
92
|
+
* Name: myapp
|
|
93
|
+
*
|
|
94
|
+
* @param {nameValues} nameValues
|
|
95
|
+
* @returns {null}
|
|
96
|
+
*/
|
|
97
|
+
function styledNameValues (nameValues) {
|
|
98
|
+
let keys = nameValues.map(nameValue => nameValue.name)
|
|
99
|
+
let keyLengths = keys.map(name => name.toString().length)
|
|
100
|
+
let maxKeyLength = Math.max.apply(Math, keyLengths) + 2
|
|
101
|
+
function pp (obj) {
|
|
102
|
+
if (typeof obj === 'string' || typeof obj === 'number') {
|
|
103
|
+
return obj
|
|
104
|
+
} else if (typeof obj === 'object') {
|
|
105
|
+
return Object.keys(obj).map(k => k + ': ' + cli.inspect(obj[k])).join(', ')
|
|
106
|
+
} else {
|
|
107
|
+
return cli.inspect(obj)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function logKeyValue (key, value) {
|
|
111
|
+
cli.log(`${key}:` + ' '.repeat(maxKeyLength - key.length - 1) + pp(value))
|
|
112
|
+
}
|
|
113
|
+
for (var nameValue of nameValues) {
|
|
114
|
+
let value = nameValue.values
|
|
115
|
+
if (Array.isArray(value)) {
|
|
116
|
+
if (value.length > 0) {
|
|
117
|
+
logKeyValue(nameValue.name, value[0])
|
|
118
|
+
for (var e of value.slice(1)) {
|
|
119
|
+
cli.log(' '.repeat(maxKeyLength) + pp(e))
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} else if (value !== null && value !== undefined) {
|
|
123
|
+
logKeyValue(nameValue.name, value)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports.styledJSON = styledJSON
|
|
129
|
+
module.exports.styledHeader = styledHeader
|
|
130
|
+
module.exports.styledObject = styledObject
|
|
131
|
+
module.exports.styledNameValues = styledNameValues
|
package/lib/table.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const stripAnsi = require('strip-ansi')
|
|
4
|
+
const cli = require('..')
|
|
5
|
+
const { ary, defaults, get, identity, keys, noop, partial, property, result, repeat } = require('lodash')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generates a Unicode table and feeds it into configured printer.
|
|
9
|
+
*
|
|
10
|
+
* Top-level arguments:
|
|
11
|
+
*
|
|
12
|
+
* @arg {Object[]} data - the records to format as a table.
|
|
13
|
+
* @arg {Object} options - configuration for the table.
|
|
14
|
+
*
|
|
15
|
+
* @arg {Object[]} [options.columns] - Options for formatting and finding values for table columns.
|
|
16
|
+
* @arg {function(string)} [options.headerAnsi] - Zero-width formattter for entire header.
|
|
17
|
+
* @arg {string} [options.colSep] - Separator between columns.
|
|
18
|
+
* @arg {function(row, options)} [options.after] - Function called after each row is printed.
|
|
19
|
+
* @arg {function(string)} [options.printLine] - Function responsible for printing to terminal.
|
|
20
|
+
* @arg {function(cells)} [options.printHeader] - Function to print header cells as a row.
|
|
21
|
+
* @arg {function(cells)} [options.printRow] - Function to print cells as a row.
|
|
22
|
+
*
|
|
23
|
+
* @arg {function(row)|string} [options.columns[].key] - Path to the value in the row or function to retrieve the pre-formatted value for the cell.
|
|
24
|
+
* @arg {function(string)} [options.columns[].label] - Header name for column.
|
|
25
|
+
* @arg {function(string, row)} [options.columns[].format] - Formatter function for column value.
|
|
26
|
+
* @arg {function(row)} [options.columns[].get] - Function to return a value to be presented in cell without formatting.
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
function table (data, options) {
|
|
30
|
+
let _defaults = {
|
|
31
|
+
colSep: ' ',
|
|
32
|
+
after: noop,
|
|
33
|
+
headerAnsi: identity,
|
|
34
|
+
printLine: cli.log,
|
|
35
|
+
printRow: function (cells) {
|
|
36
|
+
this.printLine(cells.join(this.colSep).trimRight())
|
|
37
|
+
},
|
|
38
|
+
printHeader: function (cells) {
|
|
39
|
+
this.printRow(cells.map(ary(this.headerAnsi, 1)))
|
|
40
|
+
this.printRow(cells.map(hdr => hdr.replace(/./g, '─')))
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let colDefaults = {
|
|
45
|
+
format: value => value ? value.toString() : '',
|
|
46
|
+
width: 0,
|
|
47
|
+
label: function () { return this.key.toString() },
|
|
48
|
+
|
|
49
|
+
get: function (row) {
|
|
50
|
+
let value
|
|
51
|
+
let path = result(this, 'key')
|
|
52
|
+
|
|
53
|
+
if (!path) {
|
|
54
|
+
value = row
|
|
55
|
+
} else {
|
|
56
|
+
value = get(row, path)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let formatted = this.format(value, row)
|
|
60
|
+
return formatted == null ? '' : formatted.toString()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function calcWidth (cell) {
|
|
65
|
+
let lines = stripAnsi(cell).split(/[\r\n]+/)
|
|
66
|
+
let lineLengths = lines.map(property('length'))
|
|
67
|
+
return Math.max.apply(Math, lineLengths)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function pad (string, length) {
|
|
71
|
+
let visibleLength = stripAnsi(string).length
|
|
72
|
+
let diff = length - visibleLength
|
|
73
|
+
|
|
74
|
+
return string + repeat(' ', Math.max(0, diff))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function render () {
|
|
78
|
+
let columns = options.columns || keys(data[0] || {})
|
|
79
|
+
|
|
80
|
+
if (typeof columns[0] === 'string') {
|
|
81
|
+
columns = columns.map(key => ({ key }))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let defaultsApplied = false
|
|
85
|
+
for (let row of data) {
|
|
86
|
+
row.height = 1
|
|
87
|
+
for (let col of columns) {
|
|
88
|
+
if (!defaultsApplied) defaults(col, colDefaults)
|
|
89
|
+
|
|
90
|
+
let cell = col.get(row)
|
|
91
|
+
|
|
92
|
+
col.width = Math.max(
|
|
93
|
+
result(col, 'label').length,
|
|
94
|
+
col.width,
|
|
95
|
+
calcWidth(cell)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
row.height = Math.max(
|
|
99
|
+
row.height,
|
|
100
|
+
cell.split(/[\r\n]+/).length
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
defaultsApplied = true
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (options.printHeader) {
|
|
107
|
+
options.printHeader(columns.map(function (col) {
|
|
108
|
+
let label = result(col, 'label')
|
|
109
|
+
return pad(label, col.width || label.length)
|
|
110
|
+
}))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getNthLineOfCell (n, row, col) {
|
|
114
|
+
// TODO memoize this
|
|
115
|
+
let lines = col.get(row).split(/[\r\n]+/)
|
|
116
|
+
return pad(lines[n] || '', col.width)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (let row of data) {
|
|
120
|
+
for (let i = 0; i < row.height; i++) {
|
|
121
|
+
let cells = columns.map(partial(getNthLineOfCell, i, row))
|
|
122
|
+
options.printRow(cells)
|
|
123
|
+
}
|
|
124
|
+
options.after(row, options)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
defaults(options, _defaults)
|
|
129
|
+
render()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = table
|
package/lib/util.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* promiseOrCallback will convert a function that returns a promise
|
|
5
|
+
* into one that will either make a node-style callback or return a promise
|
|
6
|
+
* based on whether or not a callback is passed in.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* prompt('input? ').then(function (input) {
|
|
10
|
+
* // deal with input
|
|
11
|
+
* })
|
|
12
|
+
* var prompt2 = promiseOrCallback(prompt)
|
|
13
|
+
* prompt('input? ', function (err, input) {
|
|
14
|
+
* // deal with input
|
|
15
|
+
* })
|
|
16
|
+
*
|
|
17
|
+
* @param {Function} fn a promise returning function to wrap
|
|
18
|
+
* @returns {Function} a function that behaves like before unless called with a callback
|
|
19
|
+
*/
|
|
20
|
+
function promiseOrCallback (fn) {
|
|
21
|
+
return function () {
|
|
22
|
+
if (typeof arguments[arguments.length - 1] === 'function') {
|
|
23
|
+
let args = Array.prototype.slice.call(arguments)
|
|
24
|
+
let callback = args.pop()
|
|
25
|
+
fn.apply(null, args).then(function () {
|
|
26
|
+
let args = Array.prototype.slice.call(arguments)
|
|
27
|
+
args.unshift(null)
|
|
28
|
+
callback.apply(null, args)
|
|
29
|
+
}).catch(function (err) {
|
|
30
|
+
callback(err)
|
|
31
|
+
})
|
|
32
|
+
} else {
|
|
33
|
+
return fn.apply(null, arguments)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
exports.promiseOrCallback = promiseOrCallback
|
package/lib/vars.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const url = require('url')
|
|
4
|
+
|
|
5
|
+
class Vars {
|
|
6
|
+
constructor (env) {
|
|
7
|
+
this.env = env
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get host () { return this.env.HEROKU_HOST || 'heroku.com' }
|
|
11
|
+
get apiUrl () { return this.host.startsWith('http') ? this.host : `https://api.${this.host}` }
|
|
12
|
+
get apiHost () {
|
|
13
|
+
if (this.host.startsWith('http')) {
|
|
14
|
+
const u = url.parse(this.host)
|
|
15
|
+
if (u.host) return u.host
|
|
16
|
+
}
|
|
17
|
+
return `api.${this.host}`
|
|
18
|
+
}
|
|
19
|
+
get httpGitHost () {
|
|
20
|
+
if (this.env.HEROKU_GIT_HOST) return this.env.HEROKU_GIT_HOST
|
|
21
|
+
if (this.host.startsWith('http')) {
|
|
22
|
+
const u = url.parse(this.host)
|
|
23
|
+
if (u.host) return u.host
|
|
24
|
+
}
|
|
25
|
+
return `git.${this.host}`
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = new Vars(process.env)
|
package/lib/yubikey.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function toggle (onoff) {
|
|
4
|
+
const cp = require('child_process')
|
|
5
|
+
if (exports.platform !== 'darwin') return
|
|
6
|
+
try {
|
|
7
|
+
cp.execSync(`osascript -e 'if application "yubiswitch" is running then tell application "yubiswitch" to ${onoff}'`, { stdio: 'inherit' })
|
|
8
|
+
} catch (err) {}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
exports.enable = () => toggle('KeyOn')
|
|
12
|
+
exports.disable = () => toggle('KeyOff')
|
|
13
|
+
|
|
14
|
+
exports.platform = process.platform
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@heroku/heroku-cli-util",
|
|
3
|
+
"description": "Set of helpful CLI utilities",
|
|
4
|
+
"version": "8.0.12",
|
|
5
|
+
"author": "Heroku",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/heroku/heroku-cli-util/issues"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@heroku-cli/color": "^1.1.3",
|
|
11
|
+
"ansi-escapes": "^3.1.0",
|
|
12
|
+
"ansi-styles": "^3.2.1",
|
|
13
|
+
"cardinal": "^2.0.1",
|
|
14
|
+
"chalk": "^2.4.1",
|
|
15
|
+
"got": "^11.8.6",
|
|
16
|
+
"heroku-client": "^3.1.0",
|
|
17
|
+
"lodash": "^4.17.10",
|
|
18
|
+
"netrc-parser": "^3.1.4",
|
|
19
|
+
"opn": "^3.0.3",
|
|
20
|
+
"strip-ansi": "^4.0.0",
|
|
21
|
+
"supports-color": "^5.4.0",
|
|
22
|
+
"tslib": "^1.9.0",
|
|
23
|
+
"tunnel-agent": "^0.6.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"chai": "^4.1.2",
|
|
27
|
+
"co": "^4.6.0",
|
|
28
|
+
"hook-std": "^0.4.0",
|
|
29
|
+
"mocha": "^10.2.0",
|
|
30
|
+
"mocha-junit-reporter": "^1.17.0",
|
|
31
|
+
"nock": "^11.9.1",
|
|
32
|
+
"nyc": "^15.1.0",
|
|
33
|
+
"proxyquire": "^1.7.11",
|
|
34
|
+
"sinon": "^1.17.7",
|
|
35
|
+
"standard": "^12.0.1",
|
|
36
|
+
"tmp": "^0.0.33",
|
|
37
|
+
"unexpected": "^11.15.1"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">= 14"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"lib",
|
|
44
|
+
"index.js"
|
|
45
|
+
],
|
|
46
|
+
"homepage": "https://github.com/heroku/heroku-cli-util",
|
|
47
|
+
"license": "ISC",
|
|
48
|
+
"main": "index.js",
|
|
49
|
+
"mocha": {
|
|
50
|
+
"require": [
|
|
51
|
+
"./test/init"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"nyc": {
|
|
55
|
+
"exclude": [
|
|
56
|
+
"lib/linewrap.js"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "git+https://github.com/heroku/heroku-cli-util.git"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"test": "mocha",
|
|
65
|
+
"posttest": "standard"
|
|
66
|
+
},
|
|
67
|
+
"standard": {
|
|
68
|
+
"env": "mocha",
|
|
69
|
+
"ignore": [
|
|
70
|
+
"lib/linewrap.js"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"directories": {
|
|
74
|
+
"lib": "lib",
|
|
75
|
+
"test": "test"
|
|
76
|
+
},
|
|
77
|
+
"keywords": [
|
|
78
|
+
"heroku-cli-util"
|
|
79
|
+
]
|
|
80
|
+
}
|