@heroku/heroku-cli-util 10.4.0-beta.0 → 10.4.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 CHANGED
@@ -77,7 +77,7 @@ console.log(color.failure('Build failed')); // Red
77
77
  console.log(color.warning('Deprecated feature')); // Orange
78
78
 
79
79
  // User/Team colors
80
- console.log(color.team('my-team')); // Cyan, bold
80
+ console.log(color.team('my-team')); // Light cyan
81
81
  console.log(color.user('user@example.com')); // Cyan
82
82
 
83
83
  // General purpose
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ import PsqlService from './utils/pg/psql.js';
11
11
  import { ago } from './ux/ago.js';
12
12
  import { alignColumns } from './ux/align-columns.js';
13
13
  import { anykey } from './ux/any-key.js';
14
+ import { confirmCommand } from './ux/confirm-command.js';
14
15
  import { toHumanReadableDataSize } from './ux/data-size.js';
15
16
  import { formatPrice } from './ux/format-price.js';
16
17
  import { openUrl } from './ux/open-url.js';
@@ -58,6 +59,7 @@ export declare const hux: {
58
59
  alignColumns: typeof alignColumns;
59
60
  anykey: typeof anykey;
60
61
  confirm: (message: string, { defaultAnswer, ms, }?: import("./ux/confirm.js").PromptInputs<boolean>) => Promise<boolean>;
62
+ confirmCommand: typeof confirmCommand;
61
63
  formatPrice: typeof formatPrice;
62
64
  openUrl: typeof openUrl;
63
65
  prompt: typeof prompt;
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import { ago } from './ux/ago.js';
11
11
  import { alignColumns } from './ux/align-columns.js';
12
12
  import { anykey } from './ux/any-key.js';
13
13
  import { confirm } from './ux/confirm.js';
14
+ import { confirmCommand } from './ux/confirm-command.js';
14
15
  import { toHumanReadableDataSize } from './ux/data-size.js';
15
16
  import { formatPrice } from './ux/format-price.js';
16
17
  import { openUrl } from './ux/open-url.js';
@@ -50,6 +51,7 @@ export const hux = {
50
51
  alignColumns,
51
52
  anykey,
52
53
  confirm,
54
+ confirmCommand,
53
55
  formatPrice,
54
56
  openUrl,
55
57
  prompt,
@@ -8,10 +8,13 @@ declare const COLORS: {
8
8
  readonly CODE_BG: 237;
9
9
  readonly CODE_FG: 255;
10
10
  readonly CYAN: "#50D3D5";
11
+ readonly CYAN_LIGHT: "#8FF5F7";
12
+ readonly GOLD: 220;
11
13
  readonly GRAY: 248;
12
14
  readonly GREEN: 40;
13
- readonly MAGENTA: "#FF8DD3";
15
+ readonly MAGENTA: "#FF22DD";
14
16
  readonly ORANGE: 214;
17
+ readonly PINK: 212;
15
18
  readonly PURPLE: 147;
16
19
  readonly RED: "#FF8787";
17
20
  readonly TEAL: 43;
@@ -25,15 +28,19 @@ export declare const addon: (text: string) => string;
25
28
  export declare const attachment: (text: string) => string;
26
29
  export declare const name: (text: string) => string;
27
30
  export declare const success: (text: string) => string;
31
+ export declare const enabled: (text: string) => string;
28
32
  export declare const failure: (text: string) => string;
33
+ export declare const error: (text: string) => string;
29
34
  export declare const warning: (text: string) => string;
30
35
  export declare const team: (text: string) => string;
31
36
  export declare const user: (text: string) => string;
32
37
  export declare const label: (text: string) => string;
33
38
  export declare const info: (text: string) => string;
34
39
  export declare const inactive: (text: string) => string;
40
+ export declare const disabled: (text: string) => string;
35
41
  export declare const command: (text: string) => string;
36
42
  export declare const code: (text: string) => string;
43
+ export declare const snippet: (text: string) => string;
37
44
  /**
38
45
  * Color palette for reference
39
46
  * Shows ANSI256 codes for most colors, hex values for magenta, red, and cyan
@@ -41,12 +48,12 @@ export declare const code: (text: string) => string;
41
48
  export declare const colorPalette: {
42
49
  readonly addon: {
43
50
  readonly name: "yellow";
44
- readonly style: "bold";
51
+ readonly style: "normal";
45
52
  readonly value: 185;
46
53
  };
47
54
  readonly app: {
48
55
  readonly name: "purple";
49
- readonly style: "bold";
56
+ readonly style: "normal";
50
57
  readonly value: 147;
51
58
  };
52
59
  readonly attachment: {
@@ -54,6 +61,11 @@ export declare const colorPalette: {
54
61
  readonly style: "normal";
55
62
  readonly value: 185;
56
63
  };
64
+ readonly code: {
65
+ readonly name: "white on dark gray";
66
+ readonly style: "bold";
67
+ readonly value: 255;
68
+ };
57
69
  readonly command: {
58
70
  readonly name: "white on dark gray";
59
71
  readonly style: "bold";
@@ -61,7 +73,7 @@ export declare const colorPalette: {
61
73
  };
62
74
  readonly datastore: {
63
75
  readonly name: "yellow";
64
- readonly style: "bold";
76
+ readonly style: "normal";
65
77
  readonly value: 185;
66
78
  };
67
79
  readonly failure: {
@@ -69,6 +81,11 @@ export declare const colorPalette: {
69
81
  readonly style: "normal";
70
82
  readonly value: "#FF8787";
71
83
  };
84
+ readonly gold: {
85
+ readonly name: "gold";
86
+ readonly style: "normal";
87
+ readonly value: 220;
88
+ };
72
89
  readonly inactive: {
73
90
  readonly name: "gray";
74
91
  readonly style: "normal";
@@ -87,16 +104,21 @@ export declare const colorPalette: {
87
104
  readonly name: {
88
105
  readonly name: "magenta";
89
106
  readonly style: "normal";
90
- readonly value: "#FF8DD3";
107
+ readonly value: "#FF22DD";
91
108
  };
92
109
  readonly pipeline: {
93
110
  readonly name: "purple";
94
111
  readonly style: "normal";
95
112
  readonly value: 147;
96
113
  };
114
+ readonly snippet: {
115
+ readonly name: "white on dark gray";
116
+ readonly style: "normal";
117
+ readonly value: 255;
118
+ };
97
119
  readonly space: {
98
120
  readonly name: "blue";
99
- readonly style: "bold";
121
+ readonly style: "normal";
100
122
  readonly value: 117;
101
123
  };
102
124
  readonly success: {
@@ -105,9 +127,9 @@ export declare const colorPalette: {
105
127
  readonly value: 40;
106
128
  };
107
129
  readonly team: {
108
- readonly name: "cyan";
109
- readonly style: "bold";
110
- readonly value: "#50D3D5";
130
+ readonly name: "light cyan";
131
+ readonly style: "normal";
132
+ readonly value: "#8FF5F7";
111
133
  };
112
134
  readonly user: {
113
135
  readonly name: "cyan";
package/dist/ux/colors.js CHANGED
@@ -14,14 +14,19 @@ const COLORS = {
14
14
  CODE_FG: 255,
15
15
  // Cyan tones (kept as hex for precise color matching)
16
16
  CYAN: '#50D3D5',
17
+ CYAN_LIGHT: '#8FF5F7',
18
+ // Gold tones (ANSI256: 220, approx. #FFD700)
19
+ GOLD: 220,
17
20
  // Gray tones (ANSI256: 248)
18
21
  GRAY: 248,
19
22
  // Green tones (ANSI256: 40)
20
23
  GREEN: 40,
21
24
  // Magenta tones (kept as hex for precise color matching)
22
- MAGENTA: '#FF8DD3',
25
+ MAGENTA: '#FF22DD',
23
26
  // Orange tones (ANSI256: 214)
24
27
  ORANGE: 214,
28
+ // Pink tones (ANSI256: 212, approx. #FF87D7 - closest match to original #FF8DD3)
29
+ PINK: 212,
25
30
  // Purple tones (ANSI256: 147 - closest match to original #ACADFF)
26
31
  PURPLE: 147,
27
32
  // Red tones (kept as hex for precise color matching)
@@ -45,45 +50,52 @@ const supportsAnsi256 = ansi.level >= 2;
45
50
  // Helper function for purple color that falls back to ANSI16 magenta when only ANSI16 is supported
46
51
  const purpleColorize = () => supportsAnsi256 ? ansi.fg(COLORS.PURPLE) : ansi.magenta;
47
52
  // Colors for entities on the Heroku platform
48
- export const app = (text) => purpleColorize().bold(`${supportsAnsi256 ? '⬢ ' : ''}${text}`);
49
- export const pipeline = (text) => purpleColorize()(text);
50
- export const space = (text) => colorize(COLORS.BLUE).bold(`${supportsAnsi256 ? '⬡ ' : ''}${text}`);
51
- export const datastore = (text) => colorize(COLORS.YELLOW).bold(`${supportsAnsi256 ? '⛁ ' : ''}${text}`);
52
- export const addon = (text) => colorize(COLORS.YELLOW).bold(text);
53
- export const attachment = (text) => colorize(COLORS.YELLOW)(text);
54
- export const name = (text) => colorize(COLORS.MAGENTA)(text);
53
+ export const app = (text) => purpleColorize()(`${supportsAnsi256 ? '⬢ ' : ''}${text}`);
54
+ export const pipeline = (text) => colorize(COLORS.MAGENTA)(text);
55
+ export const space = (text) => colorize(COLORS.BLUE)(`${supportsAnsi256 ? '⬡ ' : ''}${text}`);
56
+ export const datastore = (text) => colorize(COLORS.YELLOW)(`${supportsAnsi256 ? '⛁ ' : ''}${text}`);
57
+ export const addon = (text) => colorize(COLORS.YELLOW)(text);
58
+ export const attachment = (text) => colorize(COLORS.GOLD)(text);
59
+ export const name = (text) => colorize(COLORS.PINK)(text);
55
60
  // Status colors
56
61
  export const success = (text) => colorize(COLORS.GREEN)(text);
62
+ export const enabled = success;
57
63
  export const failure = (text) => colorize(COLORS.RED)(text);
64
+ export const error = failure;
58
65
  export const warning = (text) => colorize(COLORS.ORANGE)(text);
59
66
  // User/Team colors
60
- export const team = (text) => colorize(COLORS.CYAN).bold(text);
67
+ export const team = (text) => colorize(COLORS.CYAN_LIGHT)(text);
61
68
  export const user = (text) => colorize(COLORS.CYAN)(text);
62
69
  // General purpose colors
63
70
  export const label = (text) => ansi.bold(text);
64
71
  export const info = (text) => colorize(COLORS.TEAL)(text);
65
72
  export const inactive = (text) => colorize(COLORS.GRAY)(text);
73
+ export const disabled = inactive;
66
74
  export const command = (text) => bgColorize(COLORS.CODE_BG).fg(COLORS.CODE_FG).bold(` $ ${text} `);
67
75
  export const code = (text) => bgColorize(COLORS.CODE_BG).fg(COLORS.CODE_FG).bold(`${text}`);
76
+ export const snippet = code;
68
77
  /**
69
78
  * Color palette for reference
70
79
  * Shows ANSI256 codes for most colors, hex values for magenta, red, and cyan
71
80
  */
72
81
  export const colorPalette = {
73
- addon: { name: 'yellow', style: 'bold', value: COLORS.YELLOW },
74
- app: { name: 'purple', style: 'bold', value: COLORS.PURPLE },
82
+ addon: { name: 'yellow', style: 'normal', value: COLORS.YELLOW },
83
+ app: { name: 'purple', style: 'normal', value: COLORS.PURPLE },
75
84
  attachment: { name: 'yellow', style: 'normal', value: COLORS.YELLOW },
85
+ code: { name: 'white on dark gray', style: 'bold', value: COLORS.CODE_FG },
76
86
  command: { name: 'white on dark gray', style: 'bold', value: COLORS.CODE_FG },
77
- datastore: { name: 'yellow', style: 'bold', value: COLORS.YELLOW },
87
+ datastore: { name: 'yellow', style: 'normal', value: COLORS.YELLOW },
78
88
  failure: { name: 'red', style: 'normal', value: COLORS.RED },
89
+ gold: { name: 'gold', style: 'normal', value: COLORS.GOLD },
79
90
  inactive: { name: 'gray', style: 'normal', value: COLORS.GRAY },
80
91
  info: { name: 'teal', style: 'normal', value: COLORS.TEAL },
81
92
  label: { name: 'bright white/black', style: 'bold', value: 'default' },
82
93
  name: { name: 'magenta', style: 'normal', value: COLORS.MAGENTA },
83
94
  pipeline: { name: 'purple', style: 'normal', value: COLORS.PURPLE },
84
- space: { name: 'blue', style: 'bold', value: COLORS.BLUE },
95
+ snippet: { name: 'white on dark gray', style: 'normal', value: COLORS.CODE_FG },
96
+ space: { name: 'blue', style: 'normal', value: COLORS.BLUE },
85
97
  success: { name: 'green', style: 'normal', value: COLORS.GREEN },
86
- team: { name: 'cyan', style: 'bold', value: COLORS.CYAN },
98
+ team: { name: 'light cyan', style: 'normal', value: COLORS.CYAN_LIGHT },
87
99
  user: { name: 'cyan', style: 'normal', value: COLORS.CYAN },
88
100
  warning: { name: 'orange', style: 'normal', value: COLORS.ORANGE },
89
101
  };
@@ -0,0 +1,38 @@
1
+ import { prompt } from './prompt.js';
2
+ type ConfirmCommandOptions = {
3
+ abortedMessage?: string;
4
+ comparison: string;
5
+ confirmation?: string;
6
+ promptFunction?: typeof prompt;
7
+ warningMessage?: string;
8
+ };
9
+ /**
10
+ * @description Prompts the user to confirm a destructive action by typing a comparison string, or validates a
11
+ * pre-provided confirmation.
12
+ * @param options - Configuration options for the confirmation command.
13
+ * @param options.abortedMessage - Custom message to display when the action is aborted (default: 'Aborted.').
14
+ * @param options.comparison - The string that must be entered to confirm the action (required). Typically the app name,
15
+ * but can be other identifiers like a Private Space name.
16
+ * @param options.confirmation - Pre-provided confirmation string to validate without prompting (optional). If provided
17
+ * and matches comparison, returns immediately without prompting.
18
+ * @param options.promptFunction - Function to use for prompting (default: prompt, used for dependency injection in tests).
19
+ * @param options.warningMessage - Custom warning message to display before prompting (optional). Default message assumes
20
+ * comparison is an app name. Provide a custom message if using a different identifier.
21
+ * @returns Promise that resolves if confirmation matches, or throws an error if it doesn't.
22
+ * @throws {Error} When confirmation doesn't match the comparison string.
23
+ * @example
24
+ * // Interactive prompt
25
+ * await confirmCommand({comparison: 'my-app'})
26
+ *
27
+ * // Pre-provided confirmation (no prompt)
28
+ * await confirmCommand({comparison: 'my-app', confirmation: 'my-app'})
29
+ *
30
+ * // Custom messages
31
+ * await confirmCommand({
32
+ * comparison: 'my-app',
33
+ * warningMessage: 'This will delete your database',
34
+ * abortedMessage: 'Database deletion cancelled.'
35
+ * })
36
+ */
37
+ export declare function confirmCommand({ abortedMessage, comparison, confirmation, promptFunction, warningMessage, }: ConfirmCommandOptions): Promise<void>;
38
+ export {};
@@ -0,0 +1,56 @@
1
+ import { ux } from '@oclif/core';
2
+ import tsheredoc from 'tsheredoc';
3
+ import * as color from './colors.js';
4
+ import { prompt } from './prompt.js';
5
+ /**
6
+ * @description Prompts the user to confirm a destructive action by typing a comparison string, or validates a
7
+ * pre-provided confirmation.
8
+ * @param options - Configuration options for the confirmation command.
9
+ * @param options.abortedMessage - Custom message to display when the action is aborted (default: 'Aborted.').
10
+ * @param options.comparison - The string that must be entered to confirm the action (required). Typically the app name,
11
+ * but can be other identifiers like a Private Space name.
12
+ * @param options.confirmation - Pre-provided confirmation string to validate without prompting (optional). If provided
13
+ * and matches comparison, returns immediately without prompting.
14
+ * @param options.promptFunction - Function to use for prompting (default: prompt, used for dependency injection in tests).
15
+ * @param options.warningMessage - Custom warning message to display before prompting (optional). Default message assumes
16
+ * comparison is an app name. Provide a custom message if using a different identifier.
17
+ * @returns Promise that resolves if confirmation matches, or throws an error if it doesn't.
18
+ * @throws {Error} When confirmation doesn't match the comparison string.
19
+ * @example
20
+ * // Interactive prompt
21
+ * await confirmCommand({comparison: 'my-app'})
22
+ *
23
+ * // Pre-provided confirmation (no prompt)
24
+ * await confirmCommand({comparison: 'my-app', confirmation: 'my-app'})
25
+ *
26
+ * // Custom messages
27
+ * await confirmCommand({
28
+ * comparison: 'my-app',
29
+ * warningMessage: 'This will delete your database',
30
+ * abortedMessage: 'Database deletion cancelled.'
31
+ * })
32
+ */
33
+ export async function confirmCommand({ abortedMessage, comparison, confirmation, promptFunction = prompt, warningMessage, }) {
34
+ const heredoc = tsheredoc.default;
35
+ if (!abortedMessage) {
36
+ abortedMessage = 'Aborted.';
37
+ }
38
+ if (confirmation) {
39
+ if (confirmation === comparison)
40
+ return;
41
+ throw new Error(`Confirmation ${color.bold.red(confirmation)} did not match ${color.bold.red(comparison)}. ${abortedMessage}`);
42
+ }
43
+ if (!warningMessage) {
44
+ warningMessage = heredoc `
45
+ Destructive Action
46
+ This command will affect the app ${color.bold.red(comparison)}
47
+ `;
48
+ }
49
+ ux.warn(warningMessage);
50
+ process.stderr.write('\n');
51
+ const entered = await promptFunction(`To proceed, type ${color.bold.red(comparison)} or re-run this command with ${color.bold.red(`--confirm ${comparison}`)}`, { required: true });
52
+ if (entered === comparison) {
53
+ return;
54
+ }
55
+ throw new Error(`Confirmation did not match ${color.bold.red(comparison)}. ${abortedMessage}`);
56
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@heroku/heroku-cli-util",
4
- "version": "10.4.0-beta.0",
4
+ "version": "10.4.0",
5
5
  "description": "Set of helpful CLI utilities",
6
6
  "author": "Heroku",
7
7
  "license": "ISC",
@@ -38,13 +38,11 @@
38
38
  "mock-stdin": "^1.0.0",
39
39
  "nock": "^13.2.9",
40
40
  "open": "^11",
41
- "printf": "^0.6.1",
42
41
  "sinon": "^18.0.1",
43
42
  "sinon-chai": "^3.7.0",
44
43
  "tmp": "^0.2.5",
45
44
  "ts-node": "^10.9.2",
46
45
  "tsconfig-paths": "^4.2.0",
47
- "tsheredoc": "^1.0.1",
48
46
  "typescript": "^5.4.0"
49
47
  },
50
48
  "dependencies": {
@@ -55,6 +53,8 @@
55
53
  "ansis": "^4.1.0",
56
54
  "debug": "^4.4.0",
57
55
  "inquirer": "^12.6.1",
56
+ "printf": "^0.6.1",
57
+ "tsheredoc": "^1.0.1",
58
58
  "tunnel-ssh": "5.2.0"
59
59
  },
60
60
  "engines": {