@heroku/heroku-cli-util 8.0.15 → 9.0.0-beta.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.
- package/LICENSE +15 -3
- package/README.md +44 -241
- package/dist/index.d.ts +20 -0
- package/dist/index.js +31 -0
- 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/LICENSE
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
The ISC License (ISC)
|
|
2
|
+
|
|
3
|
+
Copyright © Heroku 2008 - 2025
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
14
|
+
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,269 +1,72 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @heroku/heroku-cli-util
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
A set of helpful CLI utilities for Heroku and oclif-based Node.js CLIs. This
|
|
4
|
+
package provides convenient wrappers and helpers for user interaction, output
|
|
5
|
+
formatting, and test utilities.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Features
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Action
|
|
16
|
-
|
|
17
|
-
```js
|
|
18
|
-
let cli = require('heroku-cli-util');
|
|
19
|
-
await cli.action('restarting dynos', async function() {
|
|
20
|
-
let app = await heroku.get(`/apps/${context.app}`);
|
|
21
|
-
await heroku.request({method: 'DELETE', path: `/apps/${app.name}/dynos`});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// restarting dynos... done
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Prompt
|
|
28
|
-
|
|
29
|
-
```js
|
|
30
|
-
let cli = require('heroku-cli-util');
|
|
31
|
-
let email = await cli.prompt('email', {});
|
|
32
|
-
console.log(`your email is: ${email}`);
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
**cli.prompt options**
|
|
36
|
-
|
|
37
|
-
```js
|
|
38
|
-
cli.prompt('email', {
|
|
39
|
-
mask: true, // mask input field after submitting
|
|
40
|
-
hide: true // mask characters while entering
|
|
41
|
-
});
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Confirm App
|
|
45
|
-
|
|
46
|
-
Supports the same async styles as `prompt()`. Errors if not confirmed.
|
|
47
|
-
|
|
48
|
-
Basic
|
|
49
|
-
|
|
50
|
-
```js
|
|
51
|
-
let cli = require('heroku-cli-util');
|
|
52
|
-
await cli.confirmApp('appname', context.flags.confirm);
|
|
53
|
-
|
|
54
|
-
// ! WARNING: Destructive Action
|
|
55
|
-
// ! This command will affect the app appname
|
|
56
|
-
// ! To proceed, type appname or re-run this command with --confirm appname
|
|
57
|
-
|
|
58
|
-
> appname
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
Custom message
|
|
62
|
-
|
|
63
|
-
```js
|
|
64
|
-
let cli = require('heroku-cli-util');
|
|
65
|
-
await cli.confirmApp('appname', context.flags.confirm, 'foo');
|
|
66
|
-
|
|
67
|
-
// ! foo
|
|
68
|
-
// ! To proceed, type appname or re-run this command with --confirm appname
|
|
69
|
-
|
|
70
|
-
> appname
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Note that you will still need to define a `confirm` flag for your command.
|
|
74
|
-
|
|
75
|
-
## Errors
|
|
76
|
-
|
|
77
|
-
```js
|
|
78
|
-
let cli = require('heroku-cli-util');
|
|
79
|
-
cli.error("App not found");
|
|
80
|
-
// ! App not found
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Warnings
|
|
84
|
-
|
|
85
|
-
```js
|
|
86
|
-
let cli = require('heroku-cli-util');
|
|
87
|
-
cli.warn("App not found");
|
|
88
|
-
// ! App not found
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Dates
|
|
92
|
-
|
|
93
|
-
```js
|
|
94
|
-
let cli = require('heroku-cli-util');
|
|
95
|
-
let d = new Date();
|
|
96
|
-
console.log(cli.formatDate(d));
|
|
97
|
-
// 2001-01-01T08:00:00.000Z
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Hush
|
|
101
|
-
|
|
102
|
-
Use hush for verbose logging when `HEROKU_DEBUG=1`.
|
|
103
|
-
|
|
104
|
-
```js
|
|
105
|
-
let cli = require('heroku-cli-util');
|
|
106
|
-
cli.hush('foo');
|
|
107
|
-
// only prints if HEROKU_DEBUG is set
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Debug
|
|
111
|
-
|
|
112
|
-
Pretty print an object.
|
|
113
|
-
|
|
114
|
-
```js
|
|
115
|
-
let cli = require('heroku-cli-util');
|
|
116
|
-
cli.debug({foo: [1,2,3]});
|
|
117
|
-
// { foo: [ 1, 2, 3 ] }
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Stylized output
|
|
121
|
-
|
|
122
|
-
Pretty print a header, hash, and JSON
|
|
123
|
-
```js
|
|
124
|
-
let cli = require('heroku-cli-util');
|
|
125
|
-
cli.styledHeader("MyApp");
|
|
126
|
-
cli.styledHash({name: "myapp", collaborators: ["user1@example.com", "user2@example.com"]});
|
|
127
|
-
cli.styledJSON({name: "myapp"});
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Produces
|
|
131
|
-
|
|
132
|
-
```
|
|
133
|
-
=== MyApp
|
|
134
|
-
Collaborators: user1@example.com
|
|
135
|
-
user1@example.com
|
|
136
|
-
Name: myapp
|
|
137
|
-
|
|
138
|
-
{
|
|
139
|
-
"name": "myapp"
|
|
140
|
-
}
|
|
141
|
-
```
|
|
9
|
+
- **User prompts and confirmations** (yes/no, input)
|
|
10
|
+
- **Styled output** (headers, JSON, objects, tables)
|
|
11
|
+
- **Wait indicators** for async operations
|
|
12
|
+
- **Test helpers** for CLI output and environment setup
|
|
142
13
|
|
|
143
|
-
##
|
|
14
|
+
## Installation
|
|
144
15
|
|
|
145
|
-
```
|
|
146
|
-
cli
|
|
147
|
-
{app: 'first-app', language: 'ruby', dyno_count: 3},
|
|
148
|
-
{app: 'second-app', language: 'node', dyno_count: 2},
|
|
149
|
-
], {
|
|
150
|
-
columns: [
|
|
151
|
-
{key: 'app'},
|
|
152
|
-
{key: 'dyno_count', label: 'Dyno Count'},
|
|
153
|
-
{key: 'language', format: language => cli.color.red(language)},
|
|
154
|
-
]
|
|
155
|
-
});
|
|
16
|
+
```bash
|
|
17
|
+
npm install @heroku/heroku-cli-util
|
|
156
18
|
```
|
|
157
19
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
```
|
|
161
|
-
app Dyno Count language
|
|
162
|
-
────────── ────────── ────────
|
|
163
|
-
first-app 3 ruby
|
|
164
|
-
second-app 2 node
|
|
165
|
-
```
|
|
20
|
+
## Usage
|
|
166
21
|
|
|
167
|
-
|
|
22
|
+
You can import the utilities you need.
|
|
168
23
|
|
|
169
|
-
|
|
24
|
+
### Output Utilities
|
|
170
25
|
|
|
171
26
|
```js
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// this
|
|
176
|
-
// text is
|
|
177
|
-
// longer
|
|
178
|
-
// than 10
|
|
179
|
-
// characters`);
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
Useful with `process.stdout.columns || 80`.
|
|
183
|
-
|
|
184
|
-
## Open Web Browser
|
|
27
|
+
// Styled header
|
|
28
|
+
import { styledHeader } from '@heroku/heroku-cli-util/dist/ux/styled-header';
|
|
29
|
+
styledHeader('My CLI Header');
|
|
185
30
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
31
|
+
// Styled JSON
|
|
32
|
+
import { styledJSON } from '@heroku/heroku-cli-util/dist/ux/styled-json';
|
|
33
|
+
styledJSON({ foo: 'bar' });
|
|
189
34
|
|
|
190
|
-
|
|
35
|
+
// Styled object
|
|
36
|
+
import { styledObject } from '@heroku/heroku-cli-util/dist/ux/styled-object';
|
|
37
|
+
styledObject({ foo: 'bar' });
|
|
191
38
|
|
|
192
|
-
|
|
39
|
+
// Table
|
|
40
|
+
import { table } from '@heroku/heroku-cli-util/dist/ux/table';
|
|
41
|
+
table([{ name: 'Alice' }, { name: 'Bob' }], { columns: [{ key: 'name' }] });
|
|
193
42
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
43
|
+
// Wait
|
|
44
|
+
import { wait } from '@heroku/heroku-cli-util/dist/ux/wait';
|
|
45
|
+
await wait('Processing...');
|
|
197
46
|
```
|
|
198
47
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
Mock stdout and stderr by using `cli.log()` and `cli.error()`.
|
|
48
|
+
### User Interaction
|
|
202
49
|
|
|
203
50
|
```js
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
cli.mockConsole();
|
|
207
|
-
cli.log('message 2'); // prints nothing
|
|
208
|
-
cli.stdout.should.eq('message 2\n');
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## Command
|
|
212
|
-
|
|
213
|
-
Used for initializing a plugin command.
|
|
214
|
-
give you an auth'ed instance of `heroku-client` and cleanly handle API exceptions.
|
|
51
|
+
import { prompt } from '@heroku/heroku-cli-util/dist/ux/prompt';
|
|
52
|
+
const name = await prompt('What is your name?');
|
|
215
53
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
```js
|
|
219
|
-
let cli = require('heroku-cli-util');
|
|
220
|
-
let co = require('co');
|
|
221
|
-
module.exports.commands = [
|
|
222
|
-
{
|
|
223
|
-
topic: 'apps',
|
|
224
|
-
command: 'info',
|
|
225
|
-
needsAuth: true,
|
|
226
|
-
needsApp: true,
|
|
227
|
-
run: cli.command(async function (context, heroku) {
|
|
228
|
-
let app = await heroku.get(`/apps/${context.app}`);
|
|
229
|
-
console.dir(app);
|
|
230
|
-
})
|
|
231
|
-
}
|
|
232
|
-
];
|
|
54
|
+
import { confirm } from '@heroku/heroku-cli-util/dist/ux/confirm';
|
|
55
|
+
const proceed = await confirm('Continue?');
|
|
233
56
|
```
|
|
234
57
|
|
|
235
|
-
|
|
58
|
+
### Test Helpers
|
|
236
59
|
|
|
237
60
|
```js
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
module.exports.commands = [
|
|
241
|
-
{
|
|
242
|
-
topic: 'apps',
|
|
243
|
-
command: 'info',
|
|
244
|
-
needsAuth: true,
|
|
245
|
-
needsApp: true,
|
|
246
|
-
run: cli.command(
|
|
247
|
-
{preauth: true},
|
|
248
|
-
async function (context, heroku) {
|
|
249
|
-
let app = await heroku.get(`/apps/${context.app}`);
|
|
250
|
-
console.dir(app);
|
|
251
|
-
}
|
|
252
|
-
)
|
|
253
|
-
}
|
|
254
|
-
];
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
If the command has a `two_factor` API error, it will ask the user for a 2fa code and retry.
|
|
258
|
-
If you set `preauth: true` it will preauth against the current app instead of just setting the header on an app. (This is necessary if you need to do more than 1 API call that will require 2fa)
|
|
259
|
-
|
|
260
|
-
## Tests
|
|
61
|
+
import { initCliTest } from '@heroku/heroku-cli-util/dist/test-helpers/init';
|
|
62
|
+
initCliTest();
|
|
261
63
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
npm test
|
|
64
|
+
import { stdout, stderr } from '@heroku/heroku-cli-util/dist/test-helpers/stub-output';
|
|
65
|
+
// Use stdout() and stderr() in your tests to capture CLI output
|
|
265
66
|
```
|
|
266
67
|
|
|
267
|
-
##
|
|
68
|
+
## Development
|
|
268
69
|
|
|
269
|
-
|
|
70
|
+
- Build: `npm run build`
|
|
71
|
+
- Test: `npm test`
|
|
72
|
+
- Lint: `npm run lint`
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { default as expectOutput } from './test-helpers/expect-output';
|
|
2
|
+
export * from './test-helpers/init';
|
|
3
|
+
export * from './test-helpers/stub-output';
|
|
4
|
+
export * from './types/errors/ambiguous';
|
|
5
|
+
export * from './types/errors/not-found';
|
|
6
|
+
export * from './types/pg/data-api';
|
|
7
|
+
export * from './types/pg/tunnel';
|
|
8
|
+
export * from './utils/addons/resolve';
|
|
9
|
+
export * from './utils/pg/bastion';
|
|
10
|
+
export * from './utils/pg/config-vars';
|
|
11
|
+
export * from './utils/pg/databases';
|
|
12
|
+
export { default as getPgHost } from './utils/pg/host';
|
|
13
|
+
export * from './utils/pg/psql';
|
|
14
|
+
export * from './ux/confirm';
|
|
15
|
+
export * from './ux/prompt';
|
|
16
|
+
export * from './ux/styled-header';
|
|
17
|
+
export * from './ux/styled-json';
|
|
18
|
+
export * from './ux/styled-object';
|
|
19
|
+
export * from './ux/table';
|
|
20
|
+
export * from './ux/wait';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPgHost = exports.expectOutput = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
// Test helpers (optional, for test environments)
|
|
6
|
+
var expect_output_1 = require("./test-helpers/expect-output");
|
|
7
|
+
Object.defineProperty(exports, "expectOutput", { enumerable: true, get: function () { return expect_output_1.default; } });
|
|
8
|
+
tslib_1.__exportStar(require("./test-helpers/init"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./test-helpers/stub-output"), exports);
|
|
10
|
+
// Types - Errors
|
|
11
|
+
tslib_1.__exportStar(require("./types/errors/ambiguous"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./types/errors/not-found"), exports);
|
|
13
|
+
// Types - PG
|
|
14
|
+
tslib_1.__exportStar(require("./types/pg/data-api"), exports);
|
|
15
|
+
tslib_1.__exportStar(require("./types/pg/tunnel"), exports);
|
|
16
|
+
tslib_1.__exportStar(require("./utils/addons/resolve"), exports);
|
|
17
|
+
// Utilities - Postgres
|
|
18
|
+
tslib_1.__exportStar(require("./utils/pg/bastion"), exports);
|
|
19
|
+
tslib_1.__exportStar(require("./utils/pg/config-vars"), exports);
|
|
20
|
+
tslib_1.__exportStar(require("./utils/pg/databases"), exports);
|
|
21
|
+
var host_1 = require("./utils/pg/host");
|
|
22
|
+
Object.defineProperty(exports, "getPgHost", { enumerable: true, get: function () { return host_1.default; } });
|
|
23
|
+
tslib_1.__exportStar(require("./utils/pg/psql"), exports);
|
|
24
|
+
// UX helpers
|
|
25
|
+
tslib_1.__exportStar(require("./ux/confirm"), exports);
|
|
26
|
+
tslib_1.__exportStar(require("./ux/prompt"), exports);
|
|
27
|
+
tslib_1.__exportStar(require("./ux/styled-header"), exports);
|
|
28
|
+
tslib_1.__exportStar(require("./ux/styled-json"), exports);
|
|
29
|
+
tslib_1.__exportStar(require("./ux/styled-object"), exports);
|
|
30
|
+
tslib_1.__exportStar(require("./ux/table"), exports);
|
|
31
|
+
tslib_1.__exportStar(require("./ux/wait"), exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
function stripIndents(str) {
|
|
5
|
+
str = str.trim().replace(/\s+$/gm, '');
|
|
6
|
+
const indent = (str.match(/^\s+[^$]/m) || [''])[0].length - 1;
|
|
7
|
+
const regexp = new RegExp(`^s{${indent}}`, 'mg');
|
|
8
|
+
return str.replace(regexp, '');
|
|
9
|
+
}
|
|
10
|
+
const expectOutput = function (actual, expected) {
|
|
11
|
+
// it can be helpful to strip all hyphens & spaces when migrating tests before perfecting
|
|
12
|
+
// use `.replace(/[\s─]/g, '')` on both actual & expected until tests pass, then remove, and paste actual into expected
|
|
13
|
+
return (0, chai_1.expect)(actual.trim().replace(/\s+$/gm, ''))
|
|
14
|
+
.to.equal(stripIndents(expected));
|
|
15
|
+
};
|
|
16
|
+
exports.default = expectOutput;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initCliTest(): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initCliTest = initCliTest;
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
// eslint-disable-next-line n/no-missing-require
|
|
6
|
+
const { color } = require('@heroku-cli/color');
|
|
7
|
+
const nock = require('nock');
|
|
8
|
+
function initCliTest() {
|
|
9
|
+
// eslint-disable-next-line no-multi-assign
|
|
10
|
+
process.env.TS_NODE_PROJECT = path.resolve('test/tsconfig.json');
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
global.columns = '120';
|
|
13
|
+
color.enabled = false;
|
|
14
|
+
nock.disableNetConnect();
|
|
15
|
+
if (process.env.ENABLE_NET_CONNECT === 'true') {
|
|
16
|
+
nock.enableNetConnect();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stdout = stdout;
|
|
4
|
+
exports.stderr = stderr;
|
|
5
|
+
const sinon = require("sinon");
|
|
6
|
+
let stdoutWriteStub;
|
|
7
|
+
let stdoutOutput = '';
|
|
8
|
+
let stderrWriteStub;
|
|
9
|
+
let stderrOutput = '';
|
|
10
|
+
beforeEach(function () {
|
|
11
|
+
stdoutOutput = '';
|
|
12
|
+
stdoutWriteStub = sinon.stub(process.stdout, 'write').callsFake((str) => {
|
|
13
|
+
stdoutOutput += str.toString();
|
|
14
|
+
return true;
|
|
15
|
+
});
|
|
16
|
+
stderrOutput = '';
|
|
17
|
+
stderrWriteStub = sinon.stub(process.stderr, 'write').callsFake((str) => {
|
|
18
|
+
stderrOutput += str.toString();
|
|
19
|
+
return true;
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
afterEach(function () {
|
|
23
|
+
stdoutWriteStub.restore();
|
|
24
|
+
stdoutOutput = '';
|
|
25
|
+
stderrWriteStub.restore();
|
|
26
|
+
stderrOutput = '';
|
|
27
|
+
});
|
|
28
|
+
function stdout() {
|
|
29
|
+
return stdoutOutput;
|
|
30
|
+
}
|
|
31
|
+
function stderr() {
|
|
32
|
+
return stderrOutput;
|
|
33
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare class AmbiguousError extends Error {
|
|
2
|
+
readonly matches: {
|
|
3
|
+
name?: string;
|
|
4
|
+
}[];
|
|
5
|
+
readonly type: string;
|
|
6
|
+
readonly body: {
|
|
7
|
+
id: string;
|
|
8
|
+
message: string;
|
|
9
|
+
};
|
|
10
|
+
readonly message: string;
|
|
11
|
+
readonly statusCode = 422;
|
|
12
|
+
constructor(matches: {
|
|
13
|
+
name?: string;
|
|
14
|
+
}[], type: string);
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AmbiguousError = void 0;
|
|
4
|
+
class AmbiguousError extends Error {
|
|
5
|
+
constructor(matches, type) {
|
|
6
|
+
super();
|
|
7
|
+
this.matches = matches;
|
|
8
|
+
this.type = type;
|
|
9
|
+
this.body = { id: 'multiple_matches', message: this.message };
|
|
10
|
+
this.statusCode = 422;
|
|
11
|
+
this.message = `Ambiguous identifier; multiple matching add-ons found: ${matches.map(match => match.name).join(', ')}.`;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.AmbiguousError = AmbiguousError;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NotFound = void 0;
|
|
4
|
+
class NotFound extends Error {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this.id = 'not_found';
|
|
8
|
+
this.message = 'Couldn\'t find that addon.';
|
|
9
|
+
this.statusCode = 404;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.NotFound = NotFound;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as Heroku from '@heroku-cli/schema';
|
|
2
|
+
export type AddOnWithRelatedData = {
|
|
3
|
+
attachment_names?: string[];
|
|
4
|
+
links?: Link[];
|
|
5
|
+
plan: Required<Heroku.AddOn['plan']>;
|
|
6
|
+
} & Required<Heroku.AddOnAttachment['addon']>;
|
|
7
|
+
export type AddOnAttachmentWithConfigVarsAndPlan = {
|
|
8
|
+
addon: AddOnWithRelatedData;
|
|
9
|
+
config_vars: Heroku.AddOn['config_vars'];
|
|
10
|
+
} & Required<Heroku.AddOnAttachment>;
|
|
11
|
+
export type Link = {
|
|
12
|
+
attachment_name?: string;
|
|
13
|
+
created_at: string;
|
|
14
|
+
message: string;
|
|
15
|
+
name: string;
|
|
16
|
+
remote?: Link;
|
|
17
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AddOnAttachment } from '@heroku-cli/schema';
|
|
2
|
+
import { Server } from 'node:net';
|
|
3
|
+
import * as createTunnel from 'tunnel-ssh';
|
|
4
|
+
import type { AddOnAttachmentWithConfigVarsAndPlan } from './data-api';
|
|
5
|
+
export type ConnectionDetails = {
|
|
6
|
+
_tunnel?: Server;
|
|
7
|
+
bastionHost?: string;
|
|
8
|
+
bastionKey?: string;
|
|
9
|
+
database: string;
|
|
10
|
+
host: string;
|
|
11
|
+
password: string;
|
|
12
|
+
pathname: string;
|
|
13
|
+
port: string;
|
|
14
|
+
url: string;
|
|
15
|
+
user: string;
|
|
16
|
+
};
|
|
17
|
+
export type ConnectionDetailsWithAttachment = {
|
|
18
|
+
attachment: Required<{
|
|
19
|
+
addon: AddOnAttachmentWithConfigVarsAndPlan;
|
|
20
|
+
} & AddOnAttachment>;
|
|
21
|
+
} & ConnectionDetails;
|
|
22
|
+
export type TunnelConfig = createTunnel.Config;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { APIClient } from '@heroku-cli/command';
|
|
2
|
+
import type { AddOnAttachment } from '@heroku-cli/schema';
|
|
3
|
+
import type { AddOnAttachmentWithConfigVarsAndPlan } from '../../types/pg/data-api';
|
|
4
|
+
export declare const appAttachment: (heroku: APIClient, app: string | undefined, id: string, options?: {
|
|
5
|
+
addon_service?: string;
|
|
6
|
+
namespace?: string;
|
|
7
|
+
}) => Promise<{
|
|
8
|
+
addon: AddOnAttachmentWithConfigVarsAndPlan;
|
|
9
|
+
} & AddOnAttachment>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.appAttachment = void 0;
|
|
4
|
+
const ambiguous_1 = require("../../types/errors/ambiguous");
|
|
5
|
+
const not_found_1 = require("../../types/errors/not-found");
|
|
6
|
+
const appAttachment = async (heroku, app, id, options = {}) => {
|
|
7
|
+
const result = await heroku.post('/actions/addon-attachments/resolve', {
|
|
8
|
+
// eslint-disable-next-line camelcase
|
|
9
|
+
body: { addon_attachment: id, addon_service: options.addon_service, app }, headers: attachmentHeaders,
|
|
10
|
+
});
|
|
11
|
+
return singularize('addon_attachment', options.namespace)(result.body);
|
|
12
|
+
};
|
|
13
|
+
exports.appAttachment = appAttachment;
|
|
14
|
+
const attachmentHeaders = {
|
|
15
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
16
|
+
'Accept-Inclusion': 'addon:plan,config_vars',
|
|
17
|
+
};
|
|
18
|
+
function singularize(type, namespace) {
|
|
19
|
+
return (matches) => {
|
|
20
|
+
if (namespace) {
|
|
21
|
+
matches = matches.filter(m => m.namespace === namespace);
|
|
22
|
+
}
|
|
23
|
+
else if (matches.length > 1) {
|
|
24
|
+
// In cases that aren't specific enough, filter by namespace
|
|
25
|
+
matches = matches.filter(m => !Reflect.has(m, 'namespace') || m.namespace === null);
|
|
26
|
+
}
|
|
27
|
+
switch (matches.length) {
|
|
28
|
+
case 0: {
|
|
29
|
+
throw new not_found_1.NotFound();
|
|
30
|
+
}
|
|
31
|
+
case 1: {
|
|
32
|
+
return matches[0];
|
|
33
|
+
}
|
|
34
|
+
default: {
|
|
35
|
+
throw new ambiguous_1.AmbiguousError(matches, type !== null && type !== void 0 ? type : '');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { APIClient } from '@heroku-cli/command';
|
|
2
|
+
import * as createTunnel from 'tunnel-ssh';
|
|
3
|
+
import { AddOnAttachmentWithConfigVarsAndPlan } from '../../types/pg/data-api';
|
|
4
|
+
import { ConnectionDetails } from '../../types/pg/tunnel';
|
|
5
|
+
import { TunnelConfig } from '../../types/pg/tunnel';
|
|
6
|
+
export declare const bastionKeyPlan: (a: AddOnAttachmentWithConfigVarsAndPlan) => boolean;
|
|
7
|
+
export declare const env: (db: ConnectionDetails) => {
|
|
8
|
+
TZ?: string;
|
|
9
|
+
PGAPPNAME: string;
|
|
10
|
+
PGSSLMODE: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function fetchConfig(heroku: APIClient, db: {
|
|
13
|
+
id: string;
|
|
14
|
+
}): Promise<import("@heroku/http-call").HTTP<{
|
|
15
|
+
host: string;
|
|
16
|
+
private_key: string;
|
|
17
|
+
}>>;
|
|
18
|
+
export declare const getBastion: (config: Record<string, string>, baseName: string) => {
|
|
19
|
+
bastionHost: string;
|
|
20
|
+
bastionKey: string;
|
|
21
|
+
} | {
|
|
22
|
+
bastionHost?: undefined;
|
|
23
|
+
bastionKey?: undefined;
|
|
24
|
+
};
|
|
25
|
+
export declare function getConfigs(db: ConnectionDetails): {
|
|
26
|
+
dbEnv: NodeJS.ProcessEnv;
|
|
27
|
+
dbTunnelConfig: createTunnel.Config;
|
|
28
|
+
};
|
|
29
|
+
export declare function sshTunnel(db: ConnectionDetails, dbTunnelConfig: TunnelConfig, timeout?: number): Promise<void | import("net").Server | null>;
|
|
30
|
+
export declare function tunnelConfig(db: ConnectionDetails): TunnelConfig;
|