@heroku/heroku-cli-util 10.7.0-beta.0 → 10.8.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.
@@ -1,6 +1,5 @@
1
1
  import debug from 'debug';
2
2
  import { EventEmitter } from 'node:events';
3
- import * as tunnelSsh from 'tunnel-ssh';
4
3
  import host from './host.js';
5
4
  const pgDebug = debug('pg');
6
5
  /**
@@ -211,6 +210,7 @@ class Timeout {
211
210
  * @returns Promise that resolves to the created local TCP Server
212
211
  */
213
212
  async function createSSHTunnelAdapter(config) {
213
+ const tunnelSsh = await import('tunnel-ssh');
214
214
  const tunnelOptions = {
215
215
  autoClose: true,
216
216
  reconnectOnError: false,
@@ -1,4 +1,4 @@
1
- import { ux } from '@oclif/core';
1
+ import { ux } from '@oclif/core/ux';
2
2
  import debug from 'debug';
3
3
  import { spawn, } from 'node:child_process';
4
4
  import { EventEmitter, once } from 'node:events';
@@ -1,4 +1,4 @@
1
- import { Errors } from '@oclif/core';
1
+ import { error } from '@oclif/core/errors';
2
2
  import * as color from './colors.js';
3
3
  function readChar() {
4
4
  return new Promise(resolve => {
@@ -15,8 +15,8 @@ export async function anykey(message) {
15
15
  const { isRaw, isTTY } = process.stdin;
16
16
  if (!message) {
17
17
  message = isTTY
18
- ? `Press any key to continue or ${color.yellow('q')} to exit`
19
- : `Press enter to continue or ${color.yellow('q')} to exit`;
18
+ ? `Press any key to continue or ${color.warning('q')} to exit`
19
+ : `Press enter to continue or ${color.warning('q')} to exit`;
20
20
  }
21
21
  if (isTTY)
22
22
  process.stdin.setRawMode(true);
@@ -27,8 +27,8 @@ export async function anykey(message) {
27
27
  process.stderr.write('\n');
28
28
  }
29
29
  if (char === 'q')
30
- Errors.error('quit');
30
+ error('quit');
31
31
  if (char === '\u0003')
32
- Errors.error('ctrl-c');
32
+ error('ctrl-c');
33
33
  return char;
34
34
  }
@@ -1,3 +1,5 @@
1
+ import { Ansis } from 'ansis';
2
+ declare const ansis: Ansis;
1
3
  /**
2
4
  * Color constants for Heroku CLI output
3
5
  * Most colors use ANSI256 codes for better compatibility with terminals that don't support TrueColor.
@@ -48,6 +50,19 @@ export declare const disabled: (text: string) => string;
48
50
  export declare const command: (text: string) => string;
49
51
  export declare const code: (text: string) => string;
50
52
  export declare const snippet: (text: string) => string;
53
+ export declare const blue: (text: string) => string;
54
+ export declare const bold: (text: string) => string;
55
+ export declare const cyan: (text: string) => string;
56
+ export declare const gold: (text: string) => string;
57
+ export declare const gray: (text: string) => string;
58
+ export declare const green: (text: string) => string;
59
+ export declare const magenta: (text: string) => string;
60
+ export declare const orange: (text: string) => string;
61
+ export declare const pink: (text: string) => string;
62
+ export declare const purple: (text: string) => string;
63
+ export declare const red: (text: string) => string;
64
+ export declare const teal: (text: string) => string;
65
+ export declare const yellow: (text: string) => string;
51
66
  /**
52
67
  * Color palette for reference
53
68
  * Shows ANSI256 codes for most colors, hex values for magenta, red, and cyan
@@ -154,8 +169,8 @@ export declare const colorPalette: {
154
169
  */
155
170
  export { COLORS };
156
171
  /**
157
- * Re-export everything from ansis as a passthrough
158
- * This gives access to all ansis functionality while adding our custom colors
172
+ * Re-export the configured ansis instance with custom TTY detection
173
+ * Use color.ansis.bold(), color.ansis.red(), etc. for direct ansis styling
174
+ * These methods respect the custom shouldEnableColors() logic
159
175
  */
160
- export * from 'ansis';
161
- export { default as ansi } from 'ansis';
176
+ export { ansis };
package/dist/ux/colors.js CHANGED
@@ -1,4 +1,30 @@
1
- import ansis from 'ansis';
1
+ import defaultAnsis, { Ansis } from 'ansis';
2
+ /**
3
+ * Check if colors should be enabled based on TTY and environment variables.
4
+ * Colors are disabled by default when output is redirected (not a TTY),
5
+ * unless FORCE_COLOR is explicitly set.
6
+ * @returns true if colors should be enabled
7
+ */
8
+ function shouldEnableColors() {
9
+ // If FORCE_COLOR is set, let ansis handle it completely
10
+ if (process.env.FORCE_COLOR !== undefined) {
11
+ return true; // use default ansis instance with its auto-detection
12
+ }
13
+ // If NO_COLOR is set, disable colors
14
+ if (process.env.NO_COLOR !== undefined && process.env.NO_COLOR !== '0') {
15
+ return false;
16
+ }
17
+ // When output is redirected (not a TTY), disable colors by default
18
+ if (!process.stdout.isTTY) {
19
+ return false;
20
+ }
21
+ return defaultAnsis.level > 0;
22
+ }
23
+ // Determine if colors are enabled, and create ansis instance with appropriate level
24
+ const colorsEnabled = shouldEnableColors();
25
+ // Create an ansis instance with level 0 (no colors) when colors are disabled
26
+ // This ensures all ansis methods return plain strings without ANSI codes
27
+ const ansis = colorsEnabled ? defaultAnsis : new Ansis(0);
2
28
  /**
3
29
  * Color constants for Heroku CLI output
4
30
  * Most colors use ANSI256 codes for better compatibility with terminals that don't support TrueColor.
@@ -63,40 +89,66 @@ const herokuTheme = {
63
89
  addon: (text) => colorize(COLORS.YELLOW)(text),
64
90
  app: (text) => purpleColorize()(`${supportsAnsi256 ? '⬢ ' : ''}${text}`),
65
91
  attachment: (text) => colorize(COLORS.GOLD)(text),
92
+ blue: (text) => colorize(COLORS.BLUE)(text),
93
+ bold: (text) => ansis.bold(text),
66
94
  code: (text) => bgColorize(COLORS.CODE_BG).fg(COLORS.CODE_FG).bold(`${text}`),
67
95
  command: (text) => bgColorize(COLORS.CODE_BG).fg(COLORS.CODE_FG).bold(` $ ${text} `),
96
+ cyan: (text) => colorize(COLORS.CYAN)(text),
68
97
  datastore: (text) => colorize(COLORS.YELLOW)(`${supportsAnsi256 ? '⛁ ' : ''}${text}`),
69
98
  failure: (text) => colorize(COLORS.RED)(text),
99
+ gold: (text) => colorize(COLORS.GOLD)(text),
100
+ gray: (text) => colorize(COLORS.GRAY)(text),
101
+ green: (text) => colorize(COLORS.GREEN)(text),
70
102
  inactive: (text) => colorize(COLORS.GRAY)(text),
71
103
  info: (text) => colorize(COLORS.TEAL)(text),
72
104
  label: (text) => ansis.bold(text),
105
+ magenta: (text) => colorize(COLORS.MAGENTA)(text),
73
106
  name: (text) => colorize(COLORS.PINK)(text),
107
+ orange: (text) => colorize(COLORS.ORANGE)(text),
108
+ pink: (text) => colorize(COLORS.PINK)(text),
74
109
  pipeline: (text) => colorize(COLORS.MAGENTA)(text),
110
+ purple: (text) => purpleColorize()(text),
111
+ red: (text) => colorize(COLORS.RED)(text),
75
112
  space: (text) => colorize(COLORS.BLUE)(`${supportsAnsi256 ? '⬡ ' : ''}${text}`),
76
113
  success: (text) => colorize(COLORS.GREEN)(text),
114
+ teal: (text) => colorize(COLORS.TEAL)(text),
77
115
  team: (text) => colorize(COLORS.CYAN_LIGHT)(text),
78
116
  user: (text) => colorize(COLORS.CYAN)(text),
79
117
  warning: (text) => colorize(COLORS.ORANGE)(text),
118
+ yellow: (text) => colorize(COLORS.YELLOW)(text),
80
119
  };
81
120
  /** Simple theme: ANSI 8 colors only, no symbols. */
82
121
  const simpleTheme = {
83
122
  addon: (text) => ansis.yellow(text),
84
123
  app: (text) => ansis.magenta(text),
85
124
  attachment: (text) => ansis.yellow(text),
125
+ blue: (text) => ansis.blue(text),
126
+ bold: (text) => ansis.bold(text),
86
127
  code: (text) => ansis.bold(text),
87
128
  command: (text) => ansis.bold(` $ ${text} `),
129
+ cyan: (text) => ansis.cyan(text),
88
130
  datastore: (text) => ansis.yellow(text),
89
131
  failure: (text) => ansis.red(text),
132
+ gold: (text) => ansis.yellow(text),
133
+ gray: (text) => ansis.gray(text),
134
+ green: (text) => ansis.green(text),
90
135
  inactive: (text) => ansis.gray(text),
91
136
  info: (text) => ansis.cyan(text),
92
137
  label: (text) => ansis.bold(text),
138
+ magenta: (text) => ansis.magenta(text),
93
139
  name: (text) => ansis.magenta(text),
140
+ orange: (text) => ansis.yellow(text),
141
+ pink: (text) => ansis.magenta(text),
94
142
  pipeline: (text) => ansis.magenta(text),
143
+ purple: (text) => ansis.magenta(text),
144
+ red: (text) => ansis.red(text),
95
145
  space: (text) => ansis.cyan(text),
96
146
  success: (text) => ansis.green(text),
147
+ teal: (text) => ansis.cyan(text),
97
148
  team: (text) => ansis.cyan(text),
98
149
  user: (text) => ansis.cyan(text),
99
150
  warning: (text) => ansis.yellow(text),
151
+ yellow: (text) => ansis.yellow(text),
100
152
  };
101
153
  const activeTheme = () => (getTheme() === 'simple' ? simpleTheme : herokuTheme);
102
154
  // Colors for entities on the Heroku platform
@@ -124,6 +176,20 @@ export const disabled = inactive;
124
176
  export const command = (text) => activeTheme().command(text);
125
177
  export const code = (text) => activeTheme().code(text);
126
178
  export const snippet = code;
179
+ // Basic color functions
180
+ export const blue = (text) => activeTheme().blue(text);
181
+ export const bold = (text) => activeTheme().bold(text);
182
+ export const cyan = (text) => activeTheme().cyan(text);
183
+ export const gold = (text) => activeTheme().gold(text);
184
+ export const gray = (text) => activeTheme().gray(text);
185
+ export const green = (text) => activeTheme().green(text);
186
+ export const magenta = (text) => activeTheme().magenta(text);
187
+ export const orange = (text) => activeTheme().orange(text);
188
+ export const pink = (text) => activeTheme().pink(text);
189
+ export const purple = (text) => activeTheme().purple(text);
190
+ export const red = (text) => activeTheme().red(text);
191
+ export const teal = (text) => activeTheme().teal(text);
192
+ export const yellow = (text) => activeTheme().yellow(text);
127
193
  /**
128
194
  * Color palette for reference
129
195
  * Shows ANSI256 codes for most colors, hex values for magenta, red, and cyan
@@ -154,8 +220,8 @@ export const colorPalette = {
154
220
  */
155
221
  export { COLORS };
156
222
  /**
157
- * Re-export everything from ansis as a passthrough
158
- * This gives access to all ansis functionality while adding our custom colors
223
+ * Re-export the configured ansis instance with custom TTY detection
224
+ * Use color.ansis.bold(), color.ansis.red(), etc. for direct ansis styling
225
+ * These methods respect the custom shouldEnableColors() logic
159
226
  */
160
- export * from 'ansis';
161
- export { default as ansi } from 'ansis';
227
+ export { ansis };
@@ -1,4 +1,4 @@
1
- import { ux } from '@oclif/core';
1
+ import { ux } from '@oclif/core/ux';
2
2
  import tsheredoc from 'tsheredoc';
3
3
  import * as color from './colors.js';
4
4
  import { prompt } from './prompt.js';
@@ -1,7 +1,7 @@
1
- import { ux } from '@oclif/core';
2
- import { createPromptModule } from 'inquirer';
3
- const prompt = createPromptModule();
1
+ import { ux } from '@oclif/core/ux';
4
2
  export const confirm = async (message, { defaultAnswer = false, ms = 10_000, } = {}) => {
3
+ const { createPromptModule } = await import('inquirer');
4
+ const prompt = createPromptModule();
5
5
  let timeoutId;
6
6
  const promptPromise = prompt([{
7
7
  default: defaultAnswer,
@@ -1,8 +1,8 @@
1
- import open from 'open';
1
+ import type open from 'open';
2
2
  import { anykey } from './any-key.js';
3
- declare const defaultDependencies: {
3
+ type Dependencies = {
4
4
  anykey: typeof anykey;
5
5
  urlOpener: typeof open;
6
6
  };
7
- export declare function openUrl(url: string, browser?: string, action?: string, dependencies?: typeof defaultDependencies): Promise<void>;
7
+ export declare function openUrl(url: string, browser?: string, action?: string, dependencies?: Dependencies): Promise<void>;
8
8
  export {};
@@ -1,23 +1,25 @@
1
- import { ux } from '@oclif/core';
2
- import open from 'open';
1
+ import { ux } from '@oclif/core/ux';
3
2
  import { anykey } from './any-key.js';
4
3
  import * as color from './colors.js';
5
- const defaultDependencies = {
6
- anykey,
7
- urlOpener: open,
8
- };
9
- export async function openUrl(url, browser, action, dependencies = defaultDependencies) {
4
+ export async function openUrl(url, browser, action, dependencies) {
5
+ if (!dependencies) {
6
+ const openModule = await import('open');
7
+ dependencies = {
8
+ anykey,
9
+ urlOpener: openModule.default,
10
+ };
11
+ }
10
12
  let browserErrorShown = false;
11
13
  const showBrowserError = (browser) => {
12
14
  if (browserErrorShown)
13
15
  return;
14
16
  ux.warn(`We can't open ${browser ?? 'your default'} browser.`
15
- + ` Use a different browser or visit ${color.cyan(url)}${action ? ` to ${action}` : ''}.`);
17
+ + ` Use a different browser or visit ${color.info(url)}${action ? ` to ${action}` : ''}.`);
16
18
  browserErrorShown = true;
17
19
  };
18
- ux.stdout(`Opening ${color.cyan(url)} in ${browser ?? 'your default'} browser…`);
20
+ ux.stdout(`Opening ${color.info(url)} in ${browser ?? 'your default'} browser…`);
19
21
  try {
20
- await dependencies.anykey(`Press any key to open up the browser${action ? ` to ${action}` : ''}, or ${color.yellow('q')} to exit`);
22
+ await dependencies.anykey(`Press any key to open up the browser${action ? ` to ${action}` : ''}, or ${color.warning('q')} to exit`);
21
23
  }
22
24
  catch (error) {
23
25
  const { message, oclif } = error;
package/dist/ux/prompt.js CHANGED
@@ -1,6 +1,6 @@
1
- import inquirer from 'inquirer';
2
1
  export async function prompt(name, options) {
3
- const { answer } = await inquirer.prompt([{
2
+ const inquirer = await import('inquirer');
3
+ const { answer } = await inquirer.default.prompt([{
4
4
  default: options?.default,
5
5
  message: name,
6
6
  name: 'answer',
@@ -1,4 +1,4 @@
1
- import { ux } from '@oclif/core';
1
+ import { ux } from '@oclif/core/ux';
2
2
  import * as color from './colors.js';
3
3
  export function styledHeader(header) {
4
4
  return ux.stdout(color.label('=== ') + color.label(header) + '\n');
@@ -1,4 +1,4 @@
1
- import { ux } from '@oclif/core';
1
+ import { ux } from '@oclif/core/ux';
2
2
  export function styledJSON(obj) {
3
3
  ux.stdout(ux.colorizeJson(obj));
4
4
  }
@@ -1,4 +1,4 @@
1
- import { ux } from '@oclif/core';
1
+ import { ux } from '@oclif/core/ux';
2
2
  import { inspect } from 'node:util';
3
3
  import * as color from './colors.js';
4
4
  function prettyPrint(obj) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@heroku/heroku-cli-util",
4
- "version": "10.7.0-beta.0",
4
+ "version": "10.8.0-beta.0",
5
5
  "description": "Set of helpful CLI utilities",
6
6
  "author": "Heroku",
7
7
  "license": "ISC",
@@ -71,7 +71,6 @@
71
71
  "mocha": "^11",
72
72
  "mock-stdin": "^1.0.0",
73
73
  "nock": "^14.0.11",
74
- "open": "^11",
75
74
  "sinon": "^20.0.0",
76
75
  "sinon-chai": "^4.0.1",
77
76
  "tmp": "^0.2.5",
@@ -86,6 +85,7 @@
86
85
  "ansis": "^4.1.0",
87
86
  "debug": "^4.4.0",
88
87
  "inquirer": "^12.6.1",
88
+ "open": "^11",
89
89
  "printf": "^0.6.1",
90
90
  "tsheredoc": "^1.0.1",
91
91
  "tunnel-ssh": "5.2.0"