@heroku/heroku-cli-util 10.2.0 → 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 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 test utilities.
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
- - **Test helpers** for CLI output and environment setup
12
+ - **Colors** for consistent, semantic CLI styling
13
13
 
14
14
  ## Installation
15
15
 
@@ -59,25 +59,35 @@ const name = await hux.prompt('What is your name?');
59
59
  const proceed = await hux.confirm('Continue?');
60
60
  ```
61
61
 
62
- ### Test Helpers
62
+ ### Colors
63
63
 
64
64
  ```js
65
- import { testHelpers } from '@heroku/heroku-cli-util';
66
-
67
- testHelpers.initCliTest();
68
-
69
- testHelpers.setupStdoutStderr();
70
- // ...run your CLI code...
71
- const output = testHelpers.stdout();
72
- const errorOutput = testHelpers.stderr();
73
- testHelpers.restoreStdoutStderr();
74
-
75
- testHelpers.expectOutput(output, 'expected output');
76
-
77
- // Run a command (see docs for details)
78
- // await testHelpers.runCommand(MyCommand, ['arg1', 'arg2']);
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
 
83
93
  #### Error Classes
package/dist/index.d.ts CHANGED
@@ -55,3 +55,4 @@ export declare const hux: {
55
55
  table: typeof table;
56
56
  wait: typeof wait;
57
57
  };
58
+ export * as color from './ux/colors.js';
package/dist/index.js CHANGED
@@ -47,3 +47,4 @@ export const hux = {
47
47
  table,
48
48
  wait,
49
49
  };
50
+ export * as color from './ux/colors.js';
@@ -10,8 +10,6 @@ export default class AddonResolver {
10
10
  }
11
11
  async resolve(addon, app, addonService) {
12
12
  const [appPart, addonPart] = addon.match(/^(.+)::(.+)$/)?.slice(1) ?? [app, addon];
13
- console.log('appPart', appPart);
14
- console.log('addonPart', addonPart);
15
13
  const { body: addons } = await this.heroku.post('/actions/addons/resolve', {
16
14
  body: {
17
15
  addon: addonPart,
@@ -1,4 +1,4 @@
1
- import { color } from '@heroku-cli/color';
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.cmd('heroku rollback')}? Use ${color.cmd('heroku addons:attach')} to create a new attachment and `
53
- + `then ${color.cmd('heroku addons:detach')} to remove the current attachment.`);
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,7 +1,7 @@
1
- import { color } from '@heroku-cli/color';
2
1
  import { HerokuAPIError } from '@heroku-cli/command/lib/api-client.js';
3
2
  import debug from 'debug';
4
3
  import { AmbiguousError } from '../../errors/ambiguous.js';
4
+ import * as color from '../../ux/colors.js';
5
5
  import AddonAttachmentResolver from '../addons/attachment-resolver.js';
6
6
  import { getAddonService, isLegacyDatabase } from '../addons/helpers.js';
7
7
  import { bastionKeyPlan, fetchBastionConfig, getBastionConfig } from './bastion.js';
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Color constants for Heroku CLI output
3
+ * Each hex value corresponds to a specific color in the design system
4
+ */
5
+ declare const COLORS: {
6
+ readonly BLUE: "#62CBF4";
7
+ readonly CODE_BG: "#3A3A3A";
8
+ readonly CODE_FG: "#FFFFFF";
9
+ readonly CYAN: "#50D3D5";
10
+ readonly GRAY: "#B6B6B6";
11
+ readonly GREEN: "#00D300";
12
+ readonly MAGENTA: "#FF8DD3";
13
+ readonly ORANGE: "#F29D00";
14
+ readonly PURPLE: "#ACADFF";
15
+ readonly RED: "#FF8787";
16
+ readonly TEAL: "#00D4AA";
17
+ readonly YELLOW: "#BFBD25";
18
+ };
19
+ /**
20
+ * Color definitions for Heroku CLI output
21
+ * Each color has a specific purpose and hex value as defined in the design system
22
+ */
23
+ export declare const app: (text: string) => string;
24
+ export declare const pipeline: (text: string) => string;
25
+ export declare const space: (text: string) => string;
26
+ export declare const datastore: (text: string) => string;
27
+ export declare const addon: (text: string) => string;
28
+ export declare const attachment: (text: string) => string;
29
+ export declare const name: (text: string) => string;
30
+ export declare const success: (text: string) => string;
31
+ export declare const failure: (text: string) => string;
32
+ export declare const warning: (text: string) => string;
33
+ export declare const team: (text: string) => string;
34
+ export declare const user: (text: string) => string;
35
+ export declare const label: (text: string) => string;
36
+ export declare const info: (text: string) => string;
37
+ export declare const inactive: (text: string) => string;
38
+ export declare const command: (text: string) => string;
39
+ export declare const code: (text: string) => string;
40
+ /**
41
+ * Color palette for reference
42
+ */
43
+ export declare const colorPalette: {
44
+ readonly addon: {
45
+ readonly hex: "#BFBD25";
46
+ readonly name: "yellow";
47
+ readonly style: "bold";
48
+ };
49
+ readonly app: {
50
+ readonly hex: "#ACADFF";
51
+ readonly name: "purple";
52
+ readonly style: "bold";
53
+ };
54
+ readonly attachment: {
55
+ readonly hex: "#BFBD25";
56
+ readonly name: "yellow";
57
+ readonly style: "normal";
58
+ };
59
+ readonly command: {
60
+ readonly hex: "#FFFFFF";
61
+ readonly name: "white on dark gray";
62
+ readonly style: "bold";
63
+ };
64
+ readonly datastore: {
65
+ readonly hex: "#BFBD25";
66
+ readonly name: "yellow";
67
+ readonly style: "bold";
68
+ };
69
+ readonly failure: {
70
+ readonly hex: "#FF8787";
71
+ readonly name: "red";
72
+ readonly style: "normal";
73
+ };
74
+ readonly inactive: {
75
+ readonly hex: "#B6B6B6";
76
+ readonly name: "gray";
77
+ readonly style: "normal";
78
+ };
79
+ readonly info: {
80
+ readonly hex: "#00D4AA";
81
+ readonly name: "teal";
82
+ readonly style: "normal";
83
+ };
84
+ readonly label: {
85
+ readonly hex: "default";
86
+ readonly name: "bright white/black";
87
+ readonly style: "bold";
88
+ };
89
+ readonly name: {
90
+ readonly hex: "#FF8DD3";
91
+ readonly name: "magenta";
92
+ readonly style: "normal";
93
+ };
94
+ readonly pipeline: {
95
+ readonly hex: "#ACADFF";
96
+ readonly name: "purple";
97
+ readonly style: "normal";
98
+ };
99
+ readonly space: {
100
+ readonly hex: "#62CBF4";
101
+ readonly name: "blue";
102
+ readonly style: "bold";
103
+ };
104
+ readonly success: {
105
+ readonly hex: "#00D300";
106
+ readonly name: "green";
107
+ readonly style: "normal";
108
+ };
109
+ readonly team: {
110
+ readonly hex: "#50D3D5";
111
+ readonly name: "cyan";
112
+ readonly style: "bold";
113
+ };
114
+ readonly user: {
115
+ readonly hex: "#50D3D5";
116
+ readonly name: "cyan";
117
+ readonly style: "normal";
118
+ };
119
+ readonly warning: {
120
+ readonly hex: "#F29D00";
121
+ readonly name: "orange";
122
+ readonly style: "normal";
123
+ };
124
+ };
125
+ /**
126
+ * Export color constants for external use
127
+ */
128
+ export { COLORS };
129
+ /**
130
+ * Re-export everything from ansis as a passthrough
131
+ * This gives access to all ansis functionality while adding our custom colors
132
+ */
133
+ export * from 'ansis';
134
+ export { default as ansi } from 'ansis';
@@ -0,0 +1,87 @@
1
+ /* eslint-disable import/no-named-as-default-member */
2
+ import ansi from 'ansis';
3
+ /**
4
+ * Color constants for Heroku CLI output
5
+ * Each hex value corresponds to a specific color in the design system
6
+ */
7
+ const COLORS = {
8
+ // Blue tones
9
+ BLUE: '#62CBF4',
10
+ // Command tones (dark gray background, white foreground)
11
+ CODE_BG: '#3A3A3A',
12
+ CODE_FG: '#FFFFFF',
13
+ // Cyan tones
14
+ CYAN: '#50D3D5',
15
+ // Gray tones
16
+ GRAY: '#B6B6B6',
17
+ // Green tones
18
+ GREEN: '#00D300',
19
+ // Magenta tones
20
+ MAGENTA: '#FF8DD3',
21
+ // Orange tones
22
+ ORANGE: '#F29D00',
23
+ // Purple tones
24
+ PURPLE: '#ACADFF',
25
+ // Red tones
26
+ RED: '#FF8787',
27
+ // Teal tones
28
+ TEAL: '#00D4AA',
29
+ // Yellow tones
30
+ YELLOW: '#BFBD25',
31
+ };
32
+ /**
33
+ * Color definitions for Heroku CLI output
34
+ * Each color has a specific purpose and hex value as defined in the design system
35
+ */
36
+ // Colors for entities on the Heroku platform
37
+ export const app = (text) => ansi.hex(COLORS.PURPLE).bold(`⬢ ${text}`);
38
+ export const pipeline = (text) => ansi.hex(COLORS.PURPLE)(text);
39
+ export const space = (text) => ansi.hex(COLORS.BLUE).bold(`⬡ ${text}`);
40
+ export const datastore = (text) => ansi.hex(COLORS.YELLOW).bold(`☷ ${text}`);
41
+ export const addon = (text) => ansi.hex(COLORS.YELLOW).bold(text);
42
+ export const attachment = (text) => ansi.hex(COLORS.YELLOW)(text);
43
+ export const name = (text) => ansi.hex(COLORS.MAGENTA)(text);
44
+ // Status colors
45
+ export const success = (text) => ansi.hex(COLORS.GREEN)(text);
46
+ export const failure = (text) => ansi.hex(COLORS.RED)(text);
47
+ export const warning = (text) => ansi.hex(COLORS.ORANGE)(text);
48
+ // User/Team colors
49
+ export const team = (text) => ansi.hex(COLORS.CYAN).bold(text);
50
+ export const user = (text) => ansi.hex(COLORS.CYAN)(text);
51
+ // General purpose colors
52
+ export const label = (text) => ansi.bold(text);
53
+ export const info = (text) => ansi.hex(COLORS.TEAL)(text);
54
+ export const inactive = (text) => ansi.hex(COLORS.GRAY)(text);
55
+ export const command = (text) => ansi.bgHex(COLORS.CODE_BG).hex(COLORS.CODE_FG).bold(` $ ${text} `);
56
+ export const code = (text) => ansi.bgHex(COLORS.CODE_BG).hex(COLORS.CODE_FG).bold(`${text} `);
57
+ /**
58
+ * Color palette for reference
59
+ */
60
+ export const colorPalette = {
61
+ addon: { hex: COLORS.YELLOW, name: 'yellow', style: 'bold' },
62
+ app: { hex: COLORS.PURPLE, name: 'purple', style: 'bold' },
63
+ attachment: { hex: COLORS.YELLOW, name: 'yellow', style: 'normal' },
64
+ command: { hex: COLORS.CODE_FG, name: 'white on dark gray', style: 'bold' },
65
+ datastore: { hex: COLORS.YELLOW, name: 'yellow', style: 'bold' },
66
+ failure: { hex: COLORS.RED, name: 'red', style: 'normal' },
67
+ inactive: { hex: COLORS.GRAY, name: 'gray', style: 'normal' },
68
+ info: { hex: COLORS.TEAL, name: 'teal', style: 'normal' },
69
+ label: { hex: 'default', name: 'bright white/black', style: 'bold' },
70
+ name: { hex: COLORS.MAGENTA, name: 'magenta', style: 'normal' },
71
+ pipeline: { hex: COLORS.PURPLE, name: 'purple', style: 'normal' },
72
+ space: { hex: COLORS.BLUE, name: 'blue', style: 'bold' },
73
+ success: { hex: COLORS.GREEN, name: 'green', style: 'normal' },
74
+ team: { hex: COLORS.CYAN, name: 'cyan', style: 'bold' },
75
+ user: { hex: COLORS.CYAN, name: 'cyan', style: 'normal' },
76
+ warning: { hex: COLORS.ORANGE, name: 'orange', style: 'normal' },
77
+ };
78
+ /**
79
+ * Export color constants for external use
80
+ */
81
+ export { COLORS };
82
+ /**
83
+ * Re-export everything from ansis as a passthrough
84
+ * This gives access to all ansis functionality while adding our custom colors
85
+ */
86
+ export * from 'ansis';
87
+ export { default as ansi } from 'ansis';
@@ -1,5 +1,5 @@
1
- import { color } from '@heroku-cli/color';
2
1
  import { ux } from '@oclif/core';
2
+ import * as color from './colors.js';
3
3
  export function styledHeader(header) {
4
- return ux.stdout(color.dim('=== ') + color.bold(header) + '\n');
4
+ return ux.stdout(color.label('=== ') + color.label(header) + '\n');
5
5
  }
@@ -1,6 +1,6 @@
1
- import { color } from '@heroku-cli/color';
2
1
  import { ux } from '@oclif/core';
3
2
  import { inspect } from 'node:util';
3
+ import * as color from './colors.js';
4
4
  function prettyPrint(obj) {
5
5
  if (!obj)
6
6
  return inspect(obj);
@@ -29,7 +29,7 @@ export function styledObject(obj, keys) {
29
29
  const output = [];
30
30
  const keyLengths = Object.keys(obj).map(key => key.toString().length);
31
31
  const maxKeyLength = Math.max(...keyLengths) + 2;
32
- const logKeyValue = (key, value) => `${color.rgb(147, 112, 219)(key)}:` + ' '.repeat(maxKeyLength - key.length - 1) + prettyPrint(value);
32
+ const logKeyValue = (key, value) => `${color.label(key)}:` + ' '.repeat(maxKeyLength - key.length - 1) + prettyPrint(value);
33
33
  for (const [key, value] of Object.entries(obj)) {
34
34
  if (keys && !keys.includes(key))
35
35
  continue;
package/dist/ux/table.js CHANGED
@@ -13,7 +13,8 @@ export function table(data, columns, options) {
13
13
  borderColor: 'whiteBright',
14
14
  borderStyle: 'headers-only-with-underline',
15
15
  headerOptions: {
16
- color: 'rgb(147, 112, 219)',
16
+ bold: true,
17
+ color: 'white', // or 'reset' to use default terminal color
17
18
  },
18
19
  }),
19
20
  ...tableOptions,
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@heroku/heroku-cli-util",
4
- "version": "10.2.0",
4
+ "version": "10.3.0",
5
5
  "description": "Set of helpful CLI utilities",
6
6
  "author": "Heroku",
7
7
  "license": "ISC",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/heroku/heroku-cli-util.git"
11
+ },
8
12
  "main": "dist/index.js",
9
13
  "types": "dist/index.d.ts",
10
14
  "files": [
@@ -36,7 +40,6 @@
36
40
  "nock": "^13.2.9",
37
41
  "sinon": "^18.0.1",
38
42
  "sinon-chai": "^3.7.0",
39
- "strip-ansi": "^6",
40
43
  "tmp": "^0.2.5",
41
44
  "ts-node": "^10.9.2",
42
45
  "tsconfig-paths": "^4.2.0",
@@ -44,11 +47,11 @@
44
47
  "typescript": "^5.4.0"
45
48
  },
46
49
  "dependencies": {
47
- "@heroku-cli/color": "^2.0.4",
48
50
  "@heroku-cli/command": "^12.0.0",
49
51
  "@heroku/http-call": "^5.5.0",
50
52
  "@oclif/core": "^4.3.0",
51
53
  "@oclif/table": "0.4.14",
54
+ "ansis": "^4.1.0",
52
55
  "debug": "^4.4.0",
53
56
  "inquirer": "^12.6.1",
54
57
  "tunnel-ssh": "5.2.0"
@@ -61,8 +64,7 @@
61
64
  "clean": "rm -rf dist",
62
65
  "example": "sh examples/run.sh",
63
66
  "lint": "eslint . --ext .ts --config .eslintrc.cjs",
64
- "prepare": "npm run build",
65
67
  "test": "c8 mocha --forbid-only \"test/**/*.test.ts\"",
66
68
  "test:local": "c8 mocha \"${npm_config_file:-test/**/*.test.+(ts|tsx)}\""
67
69
  }
68
- }
70
+ }