@heroku/heroku-cli-util 10.1.3 → 10.3.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/README.md +72 -31
- package/dist/errors/ambiguous.d.ts +3 -3
- package/dist/index.d.ts +20 -17
- package/dist/index.js +11 -20
- package/dist/types/pg/{data-api.d.ts → platform-api.d.ts} +12 -0
- package/dist/types/pg/tunnel.d.ts +2 -4
- package/dist/utils/addons/addon-resolver.d.ts +8 -0
- package/dist/utils/addons/addon-resolver.js +26 -0
- package/dist/utils/addons/{resolve.d.ts → attachment-resolver.d.ts} +1 -1
- package/dist/utils/addons/helpers.d.ts +8 -0
- package/dist/utils/addons/helpers.js +7 -0
- package/dist/utils/pg/bastion.d.ts +1 -1
- package/dist/utils/pg/config-vars.d.ts +1 -1
- package/dist/utils/pg/config-vars.js +3 -3
- package/dist/utils/pg/databases.d.ts +61 -13
- package/dist/utils/pg/databases.js +139 -53
- package/dist/utils/pg/psql.d.ts +52 -9
- package/dist/utils/pg/psql.js +137 -41
- package/dist/ux/colors.d.ts +134 -0
- package/dist/ux/colors.js +87 -0
- package/dist/ux/styled-header.js +2 -2
- package/dist/ux/styled-object.js +2 -2
- package/dist/ux/table.js +2 -1
- package/package.json +9 -5
- /package/dist/types/pg/{data-api.js → platform-api.js} +0 -0
- /package/dist/utils/addons/{resolve.js → attachment-resolver.js} +0 -0
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
A set of helpful CLI utilities for Heroku and oclif-based Node.js CLIs. This
|
|
4
4
|
package provides convenient wrappers and helpers for user interaction, output
|
|
5
|
-
formatting, and
|
|
5
|
+
formatting, and color styling.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **User prompts and confirmations** (yes/no, input)
|
|
10
10
|
- **Styled output** (headers, JSON, objects, tables)
|
|
11
11
|
- **Wait indicators** for async operations
|
|
12
|
-
- **
|
|
12
|
+
- **Colors** for consistent, semantic CLI styling
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
@@ -59,56 +59,97 @@ const name = await hux.prompt('What is your name?');
|
|
|
59
59
|
const proceed = await hux.confirm('Continue?');
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
###
|
|
62
|
+
### Colors
|
|
63
63
|
|
|
64
64
|
```js
|
|
65
|
-
import {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
65
|
+
import { color } from '@heroku/heroku-cli-util';
|
|
66
|
+
|
|
67
|
+
// App-related colors
|
|
68
|
+
console.log(color.app('my-awesome-app')); // Purple, bold with app icon
|
|
69
|
+
console.log(color.pipeline('staging')); // Purple
|
|
70
|
+
console.log(color.space('production')); // Blue, bold with space icon
|
|
71
|
+
console.log(color.addon('heroku-postgresql')); // Yellow, bold
|
|
72
|
+
console.log(color.datastore('postgresql-123')); // Yellow, bold with datastore icon
|
|
73
|
+
|
|
74
|
+
// Status colors
|
|
75
|
+
console.log(color.success('Deploy complete')); // Green
|
|
76
|
+
console.log(color.failure('Build failed')); // Red
|
|
77
|
+
console.log(color.warning('Deprecated feature')); // Orange
|
|
78
|
+
|
|
79
|
+
// User/Team colors
|
|
80
|
+
console.log(color.team('my-team')); // Cyan, bold
|
|
81
|
+
console.log(color.user('user@example.com')); // Cyan
|
|
82
|
+
|
|
83
|
+
// General purpose
|
|
84
|
+
console.log(color.label('Name')); // Bold
|
|
85
|
+
console.log(color.info('Help text')); // Teal
|
|
86
|
+
console.log(color.command('heroku apps:list')); // Command with prompt styling
|
|
79
87
|
```
|
|
80
88
|
|
|
89
|
+
See the [COLORS.md](docs/COLORS.md) documentation for the complete color palette and usage guide.
|
|
90
|
+
|
|
81
91
|
### Types
|
|
82
92
|
|
|
93
|
+
#### Error Classes
|
|
94
|
+
|
|
83
95
|
```js
|
|
84
|
-
import {
|
|
96
|
+
import { utils } from '@heroku/heroku-cli-util';
|
|
85
97
|
|
|
86
98
|
// Error types
|
|
87
99
|
try {
|
|
88
|
-
throw new
|
|
100
|
+
throw new utils.errors.AmbiguousError([{ name: 'foo' }, { name: 'bar' }], 'addon');
|
|
89
101
|
} catch (err) {
|
|
90
|
-
if (err instanceof
|
|
102
|
+
if (err instanceof utils.errors.AmbiguousError) {
|
|
91
103
|
console.error('Ambiguous:', err.message);
|
|
92
104
|
}
|
|
93
105
|
}
|
|
94
106
|
|
|
95
107
|
try {
|
|
96
|
-
throw new
|
|
108
|
+
throw new utils.errors.NotFound();
|
|
97
109
|
} catch (err) {
|
|
98
|
-
if (err instanceof
|
|
110
|
+
if (err instanceof utils.errors.NotFound) {
|
|
99
111
|
console.error('Not found:', err.message);
|
|
100
112
|
}
|
|
101
113
|
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### PostgreSQL Types (TypeScript)
|
|
117
|
+
|
|
118
|
+
Import PG types using the `pg` namespace:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import type { pg } from '@heroku/heroku-cli-util';
|
|
122
|
+
|
|
123
|
+
// Use the types
|
|
124
|
+
const connection: pg.ConnectionDetails = {
|
|
125
|
+
database: 'mydb',
|
|
126
|
+
host: 'localhost',
|
|
127
|
+
password: 'pass',
|
|
128
|
+
pathname: '/mydb',
|
|
129
|
+
port: '5432',
|
|
130
|
+
url: 'postgres://...',
|
|
131
|
+
user: 'admin'
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
function processDatabase(details: pg.ConnectionDetailsWithAttachment) {
|
|
135
|
+
// ...
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const addon: pg.AddOnWithRelatedData = { /* ... */ };
|
|
139
|
+
const link: pg.Link = { /* ... */ };
|
|
140
|
+
const tunnel: pg.TunnelConfig = { /* ... */ };
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Alternatively, you can import types directly:
|
|
102
144
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
*/
|
|
145
|
+
```typescript
|
|
146
|
+
import type {
|
|
147
|
+
ConnectionDetails,
|
|
148
|
+
AddOnWithRelatedData,
|
|
149
|
+
ExtendedAddonAttachment,
|
|
150
|
+
Link,
|
|
151
|
+
TunnelConfig
|
|
152
|
+
} from '@heroku/heroku-cli-util';
|
|
112
153
|
```
|
|
113
154
|
|
|
114
155
|
### Database and Utility Helpers
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { ExtendedAddonAttachment } from '../types/pg/
|
|
1
|
+
import type { ExtendedAddon, ExtendedAddonAttachment } from '../types/pg/platform-api.js';
|
|
2
2
|
/**
|
|
3
3
|
* This error is used internally to signal when the `AddonAttachmentResolver` cannot resolve
|
|
4
4
|
* to a single attachment.
|
|
5
5
|
*/
|
|
6
6
|
export declare class AmbiguousError extends Error {
|
|
7
|
-
readonly matches: ExtendedAddonAttachment[];
|
|
7
|
+
readonly matches: ExtendedAddon[] | ExtendedAddonAttachment[];
|
|
8
8
|
readonly type: string;
|
|
9
9
|
readonly body: {
|
|
10
10
|
id: string;
|
|
@@ -12,5 +12,5 @@ export declare class AmbiguousError extends Error {
|
|
|
12
12
|
};
|
|
13
13
|
readonly message: string;
|
|
14
14
|
readonly statusCode = 422;
|
|
15
|
-
constructor(matches: ExtendedAddonAttachment[], type: string);
|
|
15
|
+
constructor(matches: ExtendedAddon[] | ExtendedAddonAttachment[], type: string);
|
|
16
16
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type * as DataApiTypes from './types/pg/platform-api.js';
|
|
2
|
+
import type * as TunnelTypes from './types/pg/tunnel.js';
|
|
2
3
|
import { AmbiguousError } from './errors/ambiguous.js';
|
|
3
4
|
import { NotFound } from './errors/not-found.js';
|
|
4
|
-
import
|
|
5
|
-
import { ConnectionDetails, ConnectionDetailsWithAttachment, TunnelConfig } from './types/pg/tunnel.js';
|
|
5
|
+
import AddonResolver from './utils/addons/addon-resolver.js';
|
|
6
6
|
import { getPsqlConfigs, sshTunnel } from './utils/pg/bastion.js';
|
|
7
7
|
import { getConfigVarNameFromAttachment } from './utils/pg/config-vars.js';
|
|
8
8
|
import DatabaseResolver from './utils/pg/databases.js';
|
|
@@ -14,17 +14,16 @@ import { styledJSON } from './ux/styled-json.js';
|
|
|
14
14
|
import { styledObject } from './ux/styled-object.js';
|
|
15
15
|
import { table } from './ux/table.js';
|
|
16
16
|
import { wait } from './ux/wait.js';
|
|
17
|
-
export declare
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
26
|
-
};
|
|
17
|
+
export declare namespace pg {
|
|
18
|
+
type AddOnWithRelatedData = DataApiTypes.AddOnWithRelatedData;
|
|
19
|
+
type ExtendedAddon = DataApiTypes.ExtendedAddon;
|
|
20
|
+
type ExtendedAddonAttachment = DataApiTypes.ExtendedAddonAttachment;
|
|
21
|
+
type Link = DataApiTypes.Link;
|
|
22
|
+
type ConnectionDetails = TunnelTypes.ConnectionDetails;
|
|
23
|
+
type TunnelConfig = TunnelTypes.TunnelConfig;
|
|
24
|
+
}
|
|
27
25
|
export declare const utils: {
|
|
26
|
+
AddonResolver: typeof AddonResolver;
|
|
28
27
|
errors: {
|
|
29
28
|
AmbiguousError: typeof AmbiguousError;
|
|
30
29
|
NotFound: typeof NotFound;
|
|
@@ -32,12 +31,15 @@ export declare const utils: {
|
|
|
32
31
|
pg: {
|
|
33
32
|
DatabaseResolver: typeof DatabaseResolver;
|
|
34
33
|
PsqlService: typeof PsqlService;
|
|
35
|
-
|
|
36
|
-
database(heroku: APIClient, appId: string, attachmentId?: string, namespace?: string): Promise<ConnectionDetailsWithAttachment>;
|
|
37
|
-
};
|
|
34
|
+
addonService: () => string;
|
|
38
35
|
host: typeof getHost;
|
|
36
|
+
isAdvancedDatabase: (addon: DataApiTypes.ExtendedAddon | DataApiTypes.ExtendedAddonAttachment["addon"]) => boolean;
|
|
37
|
+
isAdvancedPrivateDatabase: (addon: DataApiTypes.ExtendedAddon | DataApiTypes.ExtendedAddonAttachment["addon"]) => boolean;
|
|
38
|
+
isEssentialDatabase: (addon: DataApiTypes.ExtendedAddon | DataApiTypes.ExtendedAddonAttachment["addon"]) => boolean;
|
|
39
|
+
isLegacyDatabase: (addon: DataApiTypes.ExtendedAddon | DataApiTypes.ExtendedAddonAttachment["addon"]) => boolean;
|
|
40
|
+
isLegacyEssentialDatabase: (addon: DataApiTypes.ExtendedAddon | DataApiTypes.ExtendedAddonAttachment["addon"]) => boolean;
|
|
41
|
+
isPostgresAddon: (addon: DataApiTypes.ExtendedAddon | DataApiTypes.ExtendedAddonAttachment["addon"]) => boolean;
|
|
39
42
|
psql: {
|
|
40
|
-
exec(connectionDetails: ConnectionDetailsWithAttachment, query: string, psqlCmdArgs?: string[]): Promise<string>;
|
|
41
43
|
getConfigVarNameFromAttachment: typeof getConfigVarNameFromAttachment;
|
|
42
44
|
getPsqlConfigs: typeof getPsqlConfigs;
|
|
43
45
|
sshTunnel: typeof sshTunnel;
|
|
@@ -53,3 +55,4 @@ export declare const hux: {
|
|
|
53
55
|
table: typeof table;
|
|
54
56
|
wait: typeof wait;
|
|
55
57
|
};
|
|
58
|
+
export * as color from './ux/colors.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AmbiguousError } from './errors/ambiguous.js';
|
|
2
2
|
import { NotFound } from './errors/not-found.js';
|
|
3
|
+
import AddonResolver from './utils/addons/addon-resolver.js';
|
|
4
|
+
import { getAddonService, isAdvancedDatabase, isAdvancedPrivateDatabase, isEssentialDatabase, isLegacyDatabase, isLegacyEssentialDatabase, isPostgresAddon, } from './utils/addons/helpers.js';
|
|
3
5
|
import { getPsqlConfigs, sshTunnel } from './utils/pg/bastion.js';
|
|
4
6
|
import { getConfigVarNameFromAttachment } from './utils/pg/config-vars.js';
|
|
5
7
|
import DatabaseResolver from './utils/pg/databases.js';
|
|
@@ -12,17 +14,8 @@ import { styledJSON } from './ux/styled-json.js';
|
|
|
12
14
|
import { styledObject } from './ux/styled-object.js';
|
|
13
15
|
import { table } from './ux/table.js';
|
|
14
16
|
import { wait } from './ux/wait.js';
|
|
15
|
-
export const types = {
|
|
16
|
-
pg: {
|
|
17
|
-
AddOnWithRelatedData: {},
|
|
18
|
-
ConnectionDetails: {},
|
|
19
|
-
ConnectionDetailsWithAttachment: {},
|
|
20
|
-
ExtendedAddonAttachment: {},
|
|
21
|
-
Link: {},
|
|
22
|
-
TunnelConfig: {},
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
17
|
export const utils = {
|
|
18
|
+
AddonResolver,
|
|
26
19
|
errors: {
|
|
27
20
|
AmbiguousError,
|
|
28
21
|
NotFound, // This should be NotFoundError for consistency, but we're keeping it for backwards compatibility
|
|
@@ -30,18 +23,15 @@ export const utils = {
|
|
|
30
23
|
pg: {
|
|
31
24
|
DatabaseResolver,
|
|
32
25
|
PsqlService,
|
|
33
|
-
|
|
34
|
-
database(heroku, appId, attachmentId, namespace) {
|
|
35
|
-
const databaseResolver = new DatabaseResolver(heroku);
|
|
36
|
-
return databaseResolver.getDatabase(appId, attachmentId, namespace);
|
|
37
|
-
},
|
|
38
|
-
},
|
|
26
|
+
addonService: getAddonService,
|
|
39
27
|
host: getHost,
|
|
28
|
+
isAdvancedDatabase,
|
|
29
|
+
isAdvancedPrivateDatabase,
|
|
30
|
+
isEssentialDatabase,
|
|
31
|
+
isLegacyDatabase,
|
|
32
|
+
isLegacyEssentialDatabase,
|
|
33
|
+
isPostgresAddon,
|
|
40
34
|
psql: {
|
|
41
|
-
exec(connectionDetails, query, psqlCmdArgs = []) {
|
|
42
|
-
const psqlService = new PsqlService(connectionDetails);
|
|
43
|
-
return psqlService.execQuery(query, psqlCmdArgs);
|
|
44
|
-
},
|
|
45
35
|
getConfigVarNameFromAttachment,
|
|
46
36
|
getPsqlConfigs,
|
|
47
37
|
sshTunnel,
|
|
@@ -57,3 +47,4 @@ export const hux = {
|
|
|
57
47
|
table,
|
|
58
48
|
wait,
|
|
59
49
|
};
|
|
50
|
+
export * as color from './ux/colors.js';
|
|
@@ -31,6 +31,18 @@ type AddonAttachmentWithConfigVarsInclusion = {
|
|
|
31
31
|
export type ExtendedAddonAttachment = {
|
|
32
32
|
addon: AddonDescriptorWithPlanInclusion;
|
|
33
33
|
} & AddonAttachmentWithConfigVarsInclusion;
|
|
34
|
+
/**
|
|
35
|
+
* This is the modified type for the `AddOn` we use on these lib functions because all requests made to
|
|
36
|
+
* Platform API to get add-ons, either through the Add-on List endpoint or the add-on resolver action endpoint,
|
|
37
|
+
* include the header `Accept-Expansion: addon_service,plan`.
|
|
38
|
+
*/
|
|
39
|
+
export type ExtendedAddon = {
|
|
40
|
+
addon_service: DeepRequired<Heroku.AddOnService>;
|
|
41
|
+
plan: DeepRequired<Heroku.Plan>;
|
|
42
|
+
} & DeepRequired<Heroku.AddOn>;
|
|
43
|
+
/**
|
|
44
|
+
* The next two types need review and cleanup. They're not used anywhere in this codebase yet.
|
|
45
|
+
*/
|
|
34
46
|
export type AddOnWithRelatedData = {
|
|
35
47
|
attachment_names?: string[];
|
|
36
48
|
links?: Link[];
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Server } from 'node:net';
|
|
2
|
-
import type { ExtendedAddonAttachment } from './
|
|
2
|
+
import type { ExtendedAddonAttachment } from './platform-api.js';
|
|
3
3
|
export type ConnectionDetails = {
|
|
4
4
|
_tunnel?: Server;
|
|
5
|
+
attachment?: ExtendedAddonAttachment;
|
|
5
6
|
database: string;
|
|
6
7
|
host: string;
|
|
7
8
|
password: string;
|
|
@@ -10,9 +11,6 @@ export type ConnectionDetails = {
|
|
|
10
11
|
url: string;
|
|
11
12
|
user: string;
|
|
12
13
|
} & BastionConfig;
|
|
13
|
-
export type ConnectionDetailsWithAttachment = {
|
|
14
|
-
attachment: ExtendedAddonAttachment;
|
|
15
|
-
} & ConnectionDetails;
|
|
16
14
|
export interface TunnelConfig {
|
|
17
15
|
dstHost: string;
|
|
18
16
|
dstPort: number;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { APIClient } from '@heroku-cli/command';
|
|
2
|
+
import type { ExtendedAddon } from '../../types/pg/platform-api.js';
|
|
3
|
+
export default class AddonResolver {
|
|
4
|
+
private readonly heroku;
|
|
5
|
+
private readonly addonHeaders;
|
|
6
|
+
constructor(heroku: APIClient);
|
|
7
|
+
resolve(addon: string, app?: string, addonService?: string): Promise<ExtendedAddon>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AmbiguousError } from '../../errors/ambiguous.js';
|
|
2
|
+
export default class AddonResolver {
|
|
3
|
+
heroku;
|
|
4
|
+
addonHeaders = {
|
|
5
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
6
|
+
'Accept-Expansion': 'addon_service,plan',
|
|
7
|
+
};
|
|
8
|
+
constructor(heroku) {
|
|
9
|
+
this.heroku = heroku;
|
|
10
|
+
}
|
|
11
|
+
async resolve(addon, app, addonService) {
|
|
12
|
+
const [appPart, addonPart] = addon.match(/^(.+)::(.+)$/)?.slice(1) ?? [app, addon];
|
|
13
|
+
const { body: addons } = await this.heroku.post('/actions/addons/resolve', {
|
|
14
|
+
body: {
|
|
15
|
+
addon: addonPart,
|
|
16
|
+
addon_service: addonService,
|
|
17
|
+
app: appPart,
|
|
18
|
+
},
|
|
19
|
+
headers: this.addonHeaders,
|
|
20
|
+
});
|
|
21
|
+
if (addons.length === 1) {
|
|
22
|
+
return addons[0];
|
|
23
|
+
}
|
|
24
|
+
throw new AmbiguousError(addons, 'addon');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { APIClient } from '@heroku-cli/command';
|
|
2
|
-
import type { ExtendedAddonAttachment } from '../../types/pg/
|
|
2
|
+
import type { ExtendedAddonAttachment } from '../../types/pg/platform-api.js';
|
|
3
3
|
export interface AddonAttachmentResolverOptions {
|
|
4
4
|
addonService?: string;
|
|
5
5
|
namespace?: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ExtendedAddon, ExtendedAddonAttachment } from '../../types/pg/platform-api.js';
|
|
2
|
+
export declare const getAddonService: () => string;
|
|
3
|
+
export declare const isPostgresAddon: (addon: ExtendedAddon | ExtendedAddonAttachment["addon"]) => boolean;
|
|
4
|
+
export declare const isAdvancedDatabase: (addon: ExtendedAddon | ExtendedAddonAttachment["addon"]) => boolean;
|
|
5
|
+
export declare const isAdvancedPrivateDatabase: (addon: ExtendedAddon | ExtendedAddonAttachment["addon"]) => boolean;
|
|
6
|
+
export declare const isLegacyDatabase: (addon: ExtendedAddon | ExtendedAddonAttachment["addon"]) => boolean;
|
|
7
|
+
export declare const isLegacyEssentialDatabase: (addon: ExtendedAddon | ExtendedAddonAttachment["addon"]) => boolean;
|
|
8
|
+
export declare const isEssentialDatabase: (addon: ExtendedAddon | ExtendedAddonAttachment["addon"]) => boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const getAddonService = () => process.env.HEROKU_POSTGRESQL_ADDON_NAME || process.env.HEROKU_DATA_SERVICE || 'heroku-postgresql';
|
|
2
|
+
export const isPostgresAddon = (addon) => addon.plan.name.split(':', 2)[0] === getAddonService();
|
|
3
|
+
export const isAdvancedDatabase = (addon) => isPostgresAddon(addon) && /^(advanced|performance)/.test(addon.plan.name.split(':', 2)[1]);
|
|
4
|
+
export const isAdvancedPrivateDatabase = (addon) => isAdvancedDatabase(addon) && /(private|shield)/.test(addon.plan.name.split(':', 2)[1]);
|
|
5
|
+
export const isLegacyDatabase = (addon) => isPostgresAddon(addon) && !isAdvancedDatabase(addon);
|
|
6
|
+
export const isLegacyEssentialDatabase = (addon) => isLegacyDatabase(addon) && /^(dev|basic|mini)/.test(addon.plan.name.split(':', 2)[1]);
|
|
7
|
+
export const isEssentialDatabase = (addon) => isLegacyDatabase(addon) && addon.plan.name.split(':', 2)[1].startsWith('essential');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { APIClient } from '@heroku-cli/command';
|
|
2
2
|
import { Server } from 'node:net';
|
|
3
|
-
import { ExtendedAddonAttachment } from '../../types/pg/
|
|
3
|
+
import { ExtendedAddonAttachment } from '../../types/pg/platform-api.js';
|
|
4
4
|
import { BastionConfig, ConnectionDetails, TunnelConfig } from '../../types/pg/tunnel.js';
|
|
5
5
|
/**
|
|
6
6
|
* Determines whether the attachment belongs to an add-on installed onto a non-shield Private Space.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HTTP } from '@heroku/http-call';
|
|
2
2
|
import type { APIClient } from '@heroku-cli/command';
|
|
3
|
-
import type { ExtendedAddonAttachment } from '../../types/pg/
|
|
3
|
+
import type { ExtendedAddonAttachment } from '../../types/pg/platform-api.js';
|
|
4
4
|
/**
|
|
5
5
|
* Cache of app config vars.
|
|
6
6
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as color from '../../ux/colors.js';
|
|
2
2
|
/**
|
|
3
3
|
* Cache of app config vars.
|
|
4
4
|
*/
|
|
@@ -49,8 +49,8 @@ export function getConfigVarNameFromAttachment(attachment, config = {}) {
|
|
|
49
49
|
.filter(cvn => config[cvn]?.startsWith('postgres://'));
|
|
50
50
|
if (connStringConfigVarNames.length === 0) {
|
|
51
51
|
throw new Error(`No config vars found for ${attachment.name}; perhaps they were removed as a side effect of`
|
|
52
|
-
+ ` ${color.
|
|
53
|
-
+ `then ${color.
|
|
52
|
+
+ ` ${color.command('heroku rollback')}? Use ${color.command('heroku addons:attach')} to create a new attachment and `
|
|
53
|
+
+ `then ${color.command('heroku addons:detach')} to remove the current attachment.`);
|
|
54
54
|
}
|
|
55
55
|
// Generate the default config var name and return it if it contains a database URL connection string.
|
|
56
56
|
const configVarName = `${attachment.name}_URL`;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { APIClient } from '@heroku-cli/command';
|
|
2
|
-
import type { ExtendedAddonAttachment } from '../../types/pg/
|
|
3
|
-
import type { ConnectionDetails
|
|
2
|
+
import type { ExtendedAddon, ExtendedAddonAttachment } from '../../types/pg/platform-api.js';
|
|
3
|
+
import type { ConnectionDetails } from '../../types/pg/tunnel.js';
|
|
4
4
|
import { fetchBastionConfig } from './bastion.js';
|
|
5
5
|
import { getConfig } from './config-vars.js';
|
|
6
6
|
export default class DatabaseResolver {
|
|
@@ -8,8 +8,34 @@ export default class DatabaseResolver {
|
|
|
8
8
|
private readonly getConfigFn;
|
|
9
9
|
private readonly fetchBastionConfigFn;
|
|
10
10
|
private readonly addonAttachmentResolver;
|
|
11
|
+
private readonly addonHeaders;
|
|
11
12
|
private readonly attachmentHeaders;
|
|
12
13
|
constructor(heroku: APIClient, getConfigFn?: typeof getConfig, fetchBastionConfigFn?: typeof fetchBastionConfig);
|
|
14
|
+
/**
|
|
15
|
+
* Parses a PostgreSQL connection string (or a local database name) into a ConnectionDetails object.
|
|
16
|
+
*
|
|
17
|
+
* @param connStringOrDbName - PostgreSQL connection string or local database name
|
|
18
|
+
* @returns Connection details object with parsed connection information
|
|
19
|
+
*/
|
|
20
|
+
static parsePostgresConnectionString(connStringOrDbName: string): ConnectionDetails;
|
|
21
|
+
/**
|
|
22
|
+
* Return all Heroku Postgres databases on the Legacy tiers for a given app.
|
|
23
|
+
*
|
|
24
|
+
* @param app - The name of the app to get the databases for
|
|
25
|
+
* @returns Promise resolving to all Heroku Postgres databases
|
|
26
|
+
* @throws {Error} When no legacy database add-on exists on the app
|
|
27
|
+
*/
|
|
28
|
+
getAllLegacyDatabases(app: string): Promise<Array<{
|
|
29
|
+
attachment_names?: string[];
|
|
30
|
+
} & ExtendedAddonAttachment['addon']>>;
|
|
31
|
+
/**
|
|
32
|
+
* Resolves an arbitrary legacy database add-on based on the provided app name.
|
|
33
|
+
*
|
|
34
|
+
* @param app - The name of the app to get the arbitrary legacy database for
|
|
35
|
+
* @returns Promise resolving to the arbitrary legacy database add-on
|
|
36
|
+
* @throws {Error} When no legacy database add-on exists on the app
|
|
37
|
+
*/
|
|
38
|
+
getArbitraryLegacyDB(app: string): Promise<ExtendedAddon>;
|
|
13
39
|
/**
|
|
14
40
|
* Resolves a database attachment based on the provided database identifier
|
|
15
41
|
* (attachment name, id, or config var name) and namespace (credential).
|
|
@@ -22,6 +48,14 @@ export default class DatabaseResolver {
|
|
|
22
48
|
* @throws {AmbiguousError} When multiple matching attachments are found
|
|
23
49
|
*/
|
|
24
50
|
getAttachment(appId: string, attachmentId?: string, namespace?: string): Promise<ExtendedAddonAttachment>;
|
|
51
|
+
/**
|
|
52
|
+
* Returns the connection details for a database attachment according to the app config vars.
|
|
53
|
+
*
|
|
54
|
+
* @param attachment - The attachment to get the connection details for
|
|
55
|
+
* @param config - The record of app config vars with their values
|
|
56
|
+
* @returns Connection details with attachment information
|
|
57
|
+
*/
|
|
58
|
+
getConnectionDetails(attachment: ExtendedAddonAttachment, config?: Record<string, string>): ConnectionDetails;
|
|
25
59
|
/**
|
|
26
60
|
* Returns the connection details for a database attachment resolved through the identifiers passed as
|
|
27
61
|
* arguments: appId, attachmentId and namespace (credential).
|
|
@@ -31,14 +65,14 @@ export default class DatabaseResolver {
|
|
|
31
65
|
* @param namespace - Optional namespace/credential for the attachment
|
|
32
66
|
* @returns Promise resolving to connection details with attachment information
|
|
33
67
|
*/
|
|
34
|
-
getDatabase(appId: string, attachmentId?: string, namespace?: string): Promise<
|
|
68
|
+
getDatabase(appId: string, attachmentId?: string, namespace?: string): Promise<ConnectionDetails>;
|
|
35
69
|
/**
|
|
36
|
-
*
|
|
70
|
+
* Helper function that attempts to find all Heroku Postgres attachments on a given app.
|
|
37
71
|
*
|
|
38
|
-
* @param
|
|
39
|
-
* @returns
|
|
72
|
+
* @param app - The name of the app to get the attachments for
|
|
73
|
+
* @returns Promise resolving to an array of all Heroku Postgres attachments on the app
|
|
40
74
|
*/
|
|
41
|
-
|
|
75
|
+
private allLegacyDatabaseAttachments;
|
|
42
76
|
/**
|
|
43
77
|
* Fetches all Heroku PostgreSQL add-on attachments for a given app.
|
|
44
78
|
*
|
|
@@ -51,15 +85,29 @@ export default class DatabaseResolver {
|
|
|
51
85
|
*/
|
|
52
86
|
private allPostgresAttachments;
|
|
53
87
|
/**
|
|
54
|
-
*
|
|
88
|
+
* Helper function that groups Heroku Postgres attachments by addon.
|
|
55
89
|
*
|
|
56
|
-
* @param
|
|
57
|
-
* @
|
|
58
|
-
|
|
90
|
+
* @param attachments - The attachments to group by addon
|
|
91
|
+
* @returns A record of addon IDs with their attachment names
|
|
92
|
+
*/
|
|
93
|
+
private getAttachmentNamesByAddon;
|
|
94
|
+
/**
|
|
95
|
+
* Helper function that attempts to find a single addon attachment matching the given database identifier
|
|
96
|
+
* by comparing the identifier to the config var names of all attachments on the app
|
|
97
|
+
* (attachment name, id, or config var name).
|
|
98
|
+
*
|
|
99
|
+
* This is used internally by the `getAttachment` function to handle the lookup of addon attachments.
|
|
100
|
+
* It returns either an array with a single match or an empty array if no matches are found.
|
|
101
|
+
*
|
|
102
|
+
* @param attachments - Array of attachments for the specified app ID
|
|
103
|
+
* @param appId - The ID of the app to search for attachments
|
|
104
|
+
* @param attachmentId - The database identifier to match
|
|
105
|
+
* @returns Promise resolving to either a single match or no matches
|
|
59
106
|
*/
|
|
60
|
-
|
|
107
|
+
private getAttachmentsViaConfigVarNames;
|
|
61
108
|
/**
|
|
62
109
|
* Helper function that attempts to find a single addon attachment matching the given database identifier
|
|
110
|
+
* via the add-on attachments resolver
|
|
63
111
|
* (attachment name, id, or config var name).
|
|
64
112
|
*
|
|
65
113
|
* This is used internally by the `getAttachment` function to handle the lookup of addon attachments.
|
|
@@ -72,5 +120,5 @@ export default class DatabaseResolver {
|
|
|
72
120
|
* @param namespace - Optional namespace/credential filter
|
|
73
121
|
* @returns Promise resolving to either a single match, multiple matches with error, or no matches with error
|
|
74
122
|
*/
|
|
75
|
-
private
|
|
123
|
+
private getAttachmentsViaResolver;
|
|
76
124
|
}
|