@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.
Files changed (65) hide show
  1. package/LICENSE +15 -3
  2. package/README.md +44 -241
  3. package/dist/test-helpers/expect-output.d.ts +2 -0
  4. package/dist/test-helpers/expect-output.js +16 -0
  5. package/dist/test-helpers/init.d.ts +1 -0
  6. package/dist/test-helpers/init.js +18 -0
  7. package/dist/test-helpers/stub-output.d.ts +2 -0
  8. package/dist/test-helpers/stub-output.js +33 -0
  9. package/dist/types/errors/ambiguous.d.ts +15 -0
  10. package/dist/types/errors/ambiguous.js +14 -0
  11. package/dist/types/errors/not-found.d.ts +5 -0
  12. package/dist/types/errors/not-found.js +12 -0
  13. package/dist/types/pg/data-api.d.ts +17 -0
  14. package/dist/types/pg/data-api.js +2 -0
  15. package/dist/types/pg/tunnel.d.ts +22 -0
  16. package/dist/types/pg/tunnel.js +2 -0
  17. package/dist/utils/addons/resolve.d.ts +9 -0
  18. package/dist/utils/addons/resolve.js +39 -0
  19. package/dist/utils/pg/bastion.d.ts +30 -0
  20. package/dist/utils/pg/bastion.js +122 -0
  21. package/dist/utils/pg/config-vars.d.ts +8 -0
  22. package/dist/utils/pg/config-vars.js +34 -0
  23. package/dist/utils/pg/databases.d.ts +12 -0
  24. package/dist/utils/pg/databases.js +137 -0
  25. package/dist/utils/pg/host.d.ts +1 -0
  26. package/dist/utils/pg/host.js +7 -0
  27. package/dist/utils/pg/psql.d.ts +28 -0
  28. package/dist/utils/pg/psql.js +188 -0
  29. package/dist/ux/confirm.d.ts +1 -0
  30. package/dist/ux/confirm.js +7 -0
  31. package/dist/ux/prompt.d.ts +2 -0
  32. package/dist/ux/prompt.js +7 -0
  33. package/dist/ux/styled-header.d.ts +1 -0
  34. package/dist/ux/styled-header.js +7 -0
  35. package/dist/ux/styled-json.d.ts +1 -0
  36. package/dist/ux/styled-json.js +7 -0
  37. package/dist/ux/styled-object.d.ts +1 -0
  38. package/dist/ux/styled-object.js +7 -0
  39. package/dist/ux/table.d.ts +2 -0
  40. package/dist/ux/table.js +7 -0
  41. package/dist/ux/wait.d.ts +1 -0
  42. package/dist/ux/wait.js +7 -0
  43. package/package.json +54 -55
  44. package/index.js +0 -40
  45. package/lib/action.js +0 -54
  46. package/lib/auth.js +0 -207
  47. package/lib/command.js +0 -171
  48. package/lib/console.js +0 -105
  49. package/lib/date.js +0 -18
  50. package/lib/errors.js +0 -122
  51. package/lib/exit.js +0 -42
  52. package/lib/got.js +0 -153
  53. package/lib/linewrap.js +0 -783
  54. package/lib/mutex.js +0 -41
  55. package/lib/open.js +0 -22
  56. package/lib/preauth.js +0 -26
  57. package/lib/process.js +0 -14
  58. package/lib/prompt.js +0 -150
  59. package/lib/spinner.js +0 -147
  60. package/lib/spinners.json +0 -739
  61. package/lib/styled.js +0 -131
  62. package/lib/table.js +0 -132
  63. package/lib/util.js +0 -38
  64. package/lib/vars.js +0 -29
  65. package/lib/yubikey.js +0 -14
package/LICENSE CHANGED
@@ -1,3 +1,15 @@
1
- Copyright (c) 2016, Heroku
2
- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
3
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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
- # Heroku CLI Utilities
1
+ # @heroku/heroku-cli-util
2
2
 
3
- ![GitHub Actions CI](https://github.com/heroku/heroku-cli-util/actions/workflows/ci.yml/badge.svg)
4
- [![npm version](https://badge.fury.io/js/heroku-cli-util.svg)](http://badge.fury.io/js/heroku-cli-util)
5
- [![License](https://img.shields.io/github/license/heroku/heroku-cli-util.svg)](https://github.com/heroku/heroku-cli-util/blob/master/LICENSE)
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
- Set of helpful CLI utilities
7
+ ## Features
8
8
 
9
- ## Installation
10
-
11
- ```sh
12
- npm install heroku-cli-util --save
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
- ## Table
14
+ ## Installation
144
15
 
145
- ```js
146
- cli.table([
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
- Produces:
159
-
160
- ```
161
- app Dyno Count language
162
- ────────── ────────── ────────
163
- first-app 3 ruby
164
- second-app 2 node
165
- ```
20
+ ## Usage
166
21
 
167
- ## Linewrap
22
+ You can import the utilities you need.
168
23
 
169
- Used to indent output with wrapping around words:
24
+ ### Output Utilities
170
25
 
171
26
  ```js
172
- cli.log(cli.linewrap(2, 10, 'this is text is longer than 10 characters'));
173
- // Outputs:
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
- ```js
187
- await cli.open('https://github.com');
188
- ```
31
+ // Styled JSON
32
+ import { styledJSON } from '@heroku/heroku-cli-util/dist/ux/styled-json';
33
+ styledJSON({ foo: 'bar' });
189
34
 
190
- ## HTTP calls
35
+ // Styled object
36
+ import { styledObject } from '@heroku/heroku-cli-util/dist/ux/styled-object';
37
+ styledObject({ foo: 'bar' });
191
38
 
192
- `heroku-cli-util` includes an instance of [got](https://www.npmjs.com/package/got) that will correctly use HTTP proxies.
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
- ```js
195
- let cli = require('heroku-cli-util');
196
- let rsp = await cli.got('https://google.com');
43
+ // Wait
44
+ import { wait } from '@heroku/heroku-cli-util/dist/ux/wait';
45
+ await wait('Processing...');
197
46
  ```
198
47
 
199
- ## Mocking
200
-
201
- Mock stdout and stderr by using `cli.log()` and `cli.error()`.
48
+ ### User Interaction
202
49
 
203
50
  ```js
204
- let cli = require('heroku-cli-util');
205
- cli.log('message 1'); // prints 'message 1'
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
- It expects you to return a promise chain. This is usually done with [co](https://github.com/tj/co).
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
- With options:
58
+ ### Test Helpers
236
59
 
237
60
  ```js
238
- let cli = require('heroku-cli-util');
239
- let co = require('co');
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
- ```sh
263
- npm install
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
- ## License
68
+ ## Development
268
69
 
269
- ISC
70
+ - Build: `npm run build`
71
+ - Test: `npm test`
72
+ - Lint: `npm run lint`
@@ -0,0 +1,2 @@
1
+ declare const expectOutput: (actual: string, expected: string) => Chai.Assertion;
2
+ export default expectOutput;
@@ -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,2 @@
1
+ export declare function stdout(): string;
2
+ export declare function stderr(): string;
@@ -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,5 @@
1
+ export declare class NotFound extends Error {
2
+ readonly id = "not_found";
3
+ readonly message = "Couldn't find that addon.";
4
+ readonly statusCode = 404;
5
+ }
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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;