@heroku/heroku-cli-util 10.0.0-beta.2 → 10.1.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 +1 -1
- package/dist/errors/ambiguous.d.ts +16 -0
- package/dist/{types/errors → errors}/ambiguous.js +4 -0
- package/dist/errors/not-found.d.ts +20 -0
- package/dist/errors/not-found.js +17 -0
- package/dist/index.d.ts +15 -12
- package/dist/index.js +20 -11
- package/dist/types/pg/data-api.d.ts +35 -6
- package/dist/types/pg/tunnel.d.ts +11 -8
- package/dist/utils/addons/resolve.d.ts +11 -7
- package/dist/utils/addons/resolve.js +30 -21
- package/dist/utils/pg/bastion.d.ts +57 -25
- package/dist/utils/pg/bastion.js +146 -61
- package/dist/utils/pg/config-vars.d.ts +33 -7
- package/dist/utils/pg/config-vars.js +51 -17
- package/dist/utils/pg/databases.d.ts +74 -10
- package/dist/utils/pg/databases.js +172 -107
- package/dist/utils/pg/psql.d.ts +109 -21
- package/dist/utils/pg/psql.js +226 -139
- package/dist/ux/table.d.ts +1 -1
- package/package.json +7 -5
- package/dist/types/errors/ambiguous.d.ts +0 -15
- package/dist/types/errors/not-found.d.ts +0 -5
- package/dist/types/errors/not-found.js +0 -5
package/dist/utils/pg/psql.js
CHANGED
|
@@ -3,155 +3,41 @@ import { spawn, } from 'node:child_process';
|
|
|
3
3
|
import { EventEmitter, once } from 'node:events';
|
|
4
4
|
import { Stream } from 'node:stream';
|
|
5
5
|
import { finished } from 'node:stream/promises';
|
|
6
|
-
import {
|
|
6
|
+
import { getPsqlConfigs, sshTunnel } from './bastion.js';
|
|
7
7
|
const pgDebug = debug('pg');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// eslint-disable-next-line no-async-promise-executor
|
|
12
|
-
const promise = new Promise(async (resolve, reject) => {
|
|
13
|
-
try {
|
|
14
|
-
await finished(throughStream);
|
|
15
|
-
resolve(result);
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
reject(error);
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
// eslint-disable-next-line no-return-assign
|
|
22
|
-
throughStream.on('data', chunk => result += chunk.toString());
|
|
23
|
-
inputStream.pipe(throughStream);
|
|
24
|
-
return promise;
|
|
25
|
-
}
|
|
26
|
-
export async function exec(db, query, cmdArgs = []) {
|
|
27
|
-
const configs = getConfigs(db);
|
|
28
|
-
const options = psqlQueryOptions(query, configs.dbEnv, cmdArgs);
|
|
29
|
-
return runWithTunnel(db, configs.dbTunnelConfig, options);
|
|
30
|
-
}
|
|
31
|
-
export function psqlQueryOptions(query, dbEnv, cmdArgs = []) {
|
|
32
|
-
pgDebug('Running query: %s', query.trim());
|
|
33
|
-
const psqlArgs = ['-c', query, '--set', 'sslmode=require', ...cmdArgs];
|
|
34
|
-
const childProcessOptions = {
|
|
35
|
-
stdio: ['ignore', 'pipe', 'inherit'],
|
|
36
|
-
};
|
|
37
|
-
return {
|
|
38
|
-
childProcessOptions,
|
|
39
|
-
dbEnv,
|
|
40
|
-
psqlArgs,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
export function execPSQL({ childProcessOptions, dbEnv, psqlArgs }) {
|
|
44
|
-
const options = {
|
|
45
|
-
env: dbEnv,
|
|
46
|
-
...childProcessOptions,
|
|
47
|
-
};
|
|
48
|
-
pgDebug('opening psql process');
|
|
49
|
-
const psql = spawn('psql', psqlArgs, options);
|
|
50
|
-
psql.once('spawn', () => pgDebug('psql process spawned'));
|
|
51
|
-
return psql;
|
|
52
|
-
}
|
|
53
|
-
// According to node.js docs, sending a kill to a process won't cause an error
|
|
54
|
-
// but could have unintended consequences if the PID gets reassigned:
|
|
55
|
-
// https://nodejs.org/docs/latest-v14.x/api/child_process.html#child_process_subprocess_kill_signal
|
|
56
|
-
// To be on the safe side, check if the process was already killed before sending the signal
|
|
57
|
-
function kill(childProcess, signal) {
|
|
58
|
-
if (!childProcess.killed) {
|
|
59
|
-
pgDebug('killing psql child process');
|
|
60
|
-
childProcess.kill(signal);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
export async function runWithTunnel(db, tunnelConfig, options) {
|
|
64
|
-
const tunnel = await Tunnel.connect(db, tunnelConfig);
|
|
65
|
-
pgDebug('after create tunnel');
|
|
66
|
-
const psql = execPSQL(options);
|
|
67
|
-
// interactive opens with stdio: 'inherit'
|
|
68
|
-
// which gives the child process the same stdin,stdout,stderr of the node process (global `process`)
|
|
69
|
-
// https://nodejs.org/api/child_process.html#child_process_options_stdio
|
|
70
|
-
// psql.stdout will be null in this case
|
|
71
|
-
// return a string for consistency but ideally we should return the child process from this function
|
|
72
|
-
// and let the caller decide what to do with stdin/stdout/stderr
|
|
73
|
-
const stdoutPromise = psql.stdout ? consumeStream(psql.stdout) : Promise.resolve('');
|
|
74
|
-
const cleanupSignalTraps = trapAndForwardSignalsToChildProcess(psql);
|
|
75
|
-
try {
|
|
76
|
-
pgDebug('waiting for psql or tunnel to exit');
|
|
77
|
-
// wait for either psql or tunnel to exit;
|
|
78
|
-
// the important bit is that we ensure both processes are
|
|
79
|
-
// always cleaned up in the `finally` block below
|
|
80
|
-
await Promise.race([
|
|
81
|
-
waitForPSQLExit(psql),
|
|
82
|
-
tunnel.waitForClose(),
|
|
83
|
-
]);
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
pgDebug('wait for psql or tunnel error', error);
|
|
87
|
-
throw error;
|
|
88
|
-
}
|
|
89
|
-
finally {
|
|
90
|
-
pgDebug('begin tunnel cleanup');
|
|
91
|
-
cleanupSignalTraps();
|
|
92
|
-
tunnel.close();
|
|
93
|
-
kill(psql, 'SIGKILL');
|
|
94
|
-
pgDebug('end tunnel cleanup');
|
|
95
|
-
}
|
|
96
|
-
return stdoutPromise;
|
|
97
|
-
}
|
|
98
|
-
// trap SIGINT so that ctrl+c can be used by psql without killing the
|
|
99
|
-
// parent node process.
|
|
100
|
-
// you can use ctrl+c in psql to kill running queries
|
|
101
|
-
// while keeping the psql process open.
|
|
102
|
-
// This code is to stop the parent node process (heroku CLI)
|
|
103
|
-
// from exiting. If the parent Heroku CLI node process exits, then psql will exit as it
|
|
104
|
-
// is a child process of the Heroku CLI node process.
|
|
105
|
-
export const trapAndForwardSignalsToChildProcess = (childProcess) => {
|
|
106
|
-
const signalsToTrap = ['SIGINT'];
|
|
107
|
-
const signalTraps = signalsToTrap.map(signal => {
|
|
108
|
-
process.removeAllListeners(signal);
|
|
109
|
-
const listener = () => kill(childProcess, signal);
|
|
110
|
-
process.on(signal, listener);
|
|
111
|
-
return [signal, listener];
|
|
112
|
-
});
|
|
113
|
-
// restores the built-in node ctrl+c and other handlers
|
|
114
|
-
return () => {
|
|
115
|
-
for (const [signal, listener] of signalTraps) {
|
|
116
|
-
process.removeListener(signal, listener);
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
};
|
|
120
|
-
export async function waitForPSQLExit(psql) {
|
|
121
|
-
let errorToThrow = null;
|
|
122
|
-
try {
|
|
123
|
-
const [exitCode] = await once(psql, 'close');
|
|
124
|
-
pgDebug(`psql exited with code ${exitCode}`);
|
|
125
|
-
if (exitCode > 0) {
|
|
126
|
-
errorToThrow = new Error(`psql exited with code ${exitCode}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
pgDebug('psql process error', error);
|
|
131
|
-
const { code } = error;
|
|
132
|
-
if (code === 'ENOENT') {
|
|
133
|
-
errorToThrow = new Error('The local psql command could not be located. For help installing psql, see https://devcenter.heroku.com/articles/heroku-postgresql#local-setup');
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (errorToThrow) {
|
|
137
|
-
throw errorToThrow;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
// a small wrapper around tunnel-ssh
|
|
141
|
-
// so that other code doesn't have to worry about
|
|
142
|
-
// whether there is or is not a tunnel
|
|
8
|
+
/**
|
|
9
|
+
* A small wrapper around tunnel-ssh so that other code doesn't have to worry about whether there is or is not a tunnel.
|
|
10
|
+
*/
|
|
143
11
|
export class Tunnel {
|
|
144
12
|
bastionTunnel;
|
|
145
13
|
events;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a new Tunnel instance.
|
|
16
|
+
*
|
|
17
|
+
* @param bastionTunnel - The SSH tunnel server or void if no tunnel is needed
|
|
18
|
+
*/
|
|
146
19
|
constructor(bastionTunnel) {
|
|
147
20
|
this.bastionTunnel = bastionTunnel;
|
|
148
21
|
// eslint-disable-next-line unicorn/prefer-event-target
|
|
149
22
|
this.events = new EventEmitter();
|
|
150
23
|
}
|
|
151
|
-
|
|
152
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Creates and connects to an SSH tunnel.
|
|
26
|
+
*
|
|
27
|
+
* @param connectionDetails - The database connection details with attachment information
|
|
28
|
+
* @param tunnelConfig - The tunnel configuration object
|
|
29
|
+
* @param tunnelFn - The function to create the SSH tunnel (default: sshTunnel)
|
|
30
|
+
* @returns Promise that resolves to a new Tunnel instance
|
|
31
|
+
*/
|
|
32
|
+
static async connect(connectionDetails, tunnelConfig, tunnelFn) {
|
|
33
|
+
const tunnel = await tunnelFn(connectionDetails, tunnelConfig);
|
|
153
34
|
return new Tunnel(tunnel);
|
|
154
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Closes the tunnel if it exists, or emits a fake close event if no tunnel is needed.
|
|
38
|
+
*
|
|
39
|
+
* @returns void
|
|
40
|
+
*/
|
|
155
41
|
close() {
|
|
156
42
|
if (this.bastionTunnel) {
|
|
157
43
|
pgDebug('close tunnel');
|
|
@@ -162,6 +48,12 @@ export class Tunnel {
|
|
|
162
48
|
this.events.emit('close', 0);
|
|
163
49
|
}
|
|
164
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Waits for the tunnel to close.
|
|
53
|
+
*
|
|
54
|
+
* @returns Promise that resolves when the tunnel closes
|
|
55
|
+
* @throws Error if the secure tunnel fails
|
|
56
|
+
*/
|
|
165
57
|
async waitForClose() {
|
|
166
58
|
if (this.bastionTunnel) {
|
|
167
59
|
try {
|
|
@@ -175,8 +67,203 @@ export class Tunnel {
|
|
|
175
67
|
}
|
|
176
68
|
}
|
|
177
69
|
else {
|
|
178
|
-
pgDebug('no
|
|
70
|
+
pgDebug('no tunnel required; waiting for fake close event');
|
|
179
71
|
await once(this.events, 'close');
|
|
180
72
|
}
|
|
181
73
|
}
|
|
182
74
|
}
|
|
75
|
+
export default class PsqlService {
|
|
76
|
+
connectionDetails;
|
|
77
|
+
getPsqlConfigsFn;
|
|
78
|
+
spawnFn;
|
|
79
|
+
tunnelFn;
|
|
80
|
+
constructor(connectionDetails, getPsqlConfigsFn = getPsqlConfigs, spawnFn = spawn, tunnelFn = sshTunnel) {
|
|
81
|
+
this.connectionDetails = connectionDetails;
|
|
82
|
+
this.getPsqlConfigsFn = getPsqlConfigsFn;
|
|
83
|
+
this.spawnFn = spawnFn;
|
|
84
|
+
this.tunnelFn = tunnelFn;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Executes a PostgreSQL query using the instance's database connection details.
|
|
88
|
+
* It uses the `getPsqlConfigs` function to get the configuration for the database and the tunnel,
|
|
89
|
+
* and then calls the `runWithTunnel` function to execute the query.
|
|
90
|
+
*
|
|
91
|
+
* @param query - The SQL query to execute
|
|
92
|
+
* @param psqlCmdArgs - Additional command-line arguments for psql (default: [])
|
|
93
|
+
* @returns Promise that resolves to the query result as a string
|
|
94
|
+
*/
|
|
95
|
+
async execQuery(query, psqlCmdArgs = []) {
|
|
96
|
+
const configs = this.getPsqlConfigsFn(this.connectionDetails);
|
|
97
|
+
const options = this.psqlQueryOptions(query, configs.dbEnv, psqlCmdArgs);
|
|
98
|
+
return this.runWithTunnel(configs.dbTunnelConfig, options);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Consumes a stream and returns its content as a string.
|
|
102
|
+
*
|
|
103
|
+
* @param inputStream - The input stream to consume
|
|
104
|
+
* @returns Promise that resolves to the stream content as a string
|
|
105
|
+
*/
|
|
106
|
+
consumeStream(inputStream) {
|
|
107
|
+
let result = '';
|
|
108
|
+
const throughStream = new Stream.PassThrough();
|
|
109
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
110
|
+
const promise = new Promise(async (resolve, reject) => {
|
|
111
|
+
try {
|
|
112
|
+
await finished(throughStream);
|
|
113
|
+
resolve(result);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
reject(error);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// eslint-disable-next-line no-return-assign
|
|
120
|
+
throughStream.on('data', chunk => result += chunk.toString());
|
|
121
|
+
inputStream.pipe(throughStream);
|
|
122
|
+
return promise;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Kills a child process if it hasn't been killed already.
|
|
126
|
+
* According to node.js docs, sending a kill to a process won't cause an error
|
|
127
|
+
* but could have unintended consequences if the PID gets reassigned.
|
|
128
|
+
* To be on the safe side, check if the process was already killed before sending the signal.
|
|
129
|
+
*
|
|
130
|
+
* @param childProcess - The child process to kill
|
|
131
|
+
* @param signal - The signal to send to the process
|
|
132
|
+
* @returns void
|
|
133
|
+
*/
|
|
134
|
+
kill(childProcess, signal) {
|
|
135
|
+
if (!childProcess.killed) {
|
|
136
|
+
pgDebug('killing psql child process');
|
|
137
|
+
childProcess.kill(signal);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Creates the options for spawning the psql process.
|
|
142
|
+
*
|
|
143
|
+
* @param query - The SQL query to execute
|
|
144
|
+
* @param dbEnv - The database environment variables
|
|
145
|
+
* @param psqlCmdArgs - Additional command-line arguments for psql (default: [])
|
|
146
|
+
* @returns Object containing child process options, database environment, and psql arguments
|
|
147
|
+
*/
|
|
148
|
+
psqlQueryOptions(query, dbEnv, psqlCmdArgs = []) {
|
|
149
|
+
pgDebug('Running query: %s', query.trim());
|
|
150
|
+
const psqlArgs = ['-c', query, '--set', 'sslmode=require', ...psqlCmdArgs];
|
|
151
|
+
const childProcessOptions = {
|
|
152
|
+
stdio: ['ignore', 'pipe', 'inherit'],
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
childProcessOptions,
|
|
156
|
+
dbEnv,
|
|
157
|
+
psqlArgs,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Runs the psql command with tunnel support.
|
|
162
|
+
*
|
|
163
|
+
* @param tunnelConfig - The tunnel configuration object
|
|
164
|
+
* @param options - The options for spawning the psql process
|
|
165
|
+
* @returns Promise that resolves to the query result as a string
|
|
166
|
+
*/
|
|
167
|
+
async runWithTunnel(tunnelConfig, options) {
|
|
168
|
+
const tunnel = await Tunnel.connect(this.connectionDetails, tunnelConfig, this.tunnelFn);
|
|
169
|
+
pgDebug('after create tunnel');
|
|
170
|
+
const psql = this.spawnPsql(options);
|
|
171
|
+
// Note: In non-interactive mode, psql.stdout is available for capturing output.
|
|
172
|
+
// In interactive mode, stdio: 'inherit' would make psql.stdout null.
|
|
173
|
+
// Return a string for consistency but ideally we should return the child process from this function
|
|
174
|
+
// and let the caller decide what to do with stdin/stdout/stderr
|
|
175
|
+
const stdoutPromise = psql.stdout ? this.consumeStream(psql.stdout) : Promise.resolve('');
|
|
176
|
+
const cleanupSignalTraps = this.trapAndForwardSignalsToChildProcess(psql);
|
|
177
|
+
try {
|
|
178
|
+
pgDebug('waiting for psql or tunnel to exit');
|
|
179
|
+
// wait for either psql or tunnel to exit;
|
|
180
|
+
// the important bit is that we ensure both processes are
|
|
181
|
+
// always cleaned up in the `finally` block below
|
|
182
|
+
await Promise.race([
|
|
183
|
+
this.waitForPSQLExit(psql),
|
|
184
|
+
tunnel.waitForClose(),
|
|
185
|
+
]);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
pgDebug('wait for psql or tunnel error', error);
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
pgDebug('begin tunnel cleanup');
|
|
193
|
+
cleanupSignalTraps();
|
|
194
|
+
tunnel.close();
|
|
195
|
+
this.kill(psql, 'SIGKILL');
|
|
196
|
+
pgDebug('end tunnel cleanup');
|
|
197
|
+
}
|
|
198
|
+
return stdoutPromise;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Spawns the psql process with the given options.
|
|
202
|
+
*
|
|
203
|
+
* @param options - The options for spawning the psql process
|
|
204
|
+
* @returns The spawned child process
|
|
205
|
+
*/
|
|
206
|
+
spawnPsql(options) {
|
|
207
|
+
const { childProcessOptions, dbEnv, psqlArgs } = options;
|
|
208
|
+
const spawnOptions = {
|
|
209
|
+
env: dbEnv,
|
|
210
|
+
...childProcessOptions,
|
|
211
|
+
};
|
|
212
|
+
pgDebug('opening psql process');
|
|
213
|
+
const psql = this.spawnFn('psql', psqlArgs, spawnOptions);
|
|
214
|
+
psql.once('spawn', () => pgDebug('psql process spawned'));
|
|
215
|
+
return psql;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Traps SIGINT so that ctrl+c can be used by psql without killing the parent node process.
|
|
219
|
+
* You can use ctrl+c in psql to kill running queries while keeping the psql process open.
|
|
220
|
+
* This code is to stop the parent node process (heroku CLI) from exiting.
|
|
221
|
+
* If the parent Heroku CLI node process exits, then psql will exit as it is a child process.
|
|
222
|
+
*
|
|
223
|
+
* @param childProcess - The child process to forward signals to
|
|
224
|
+
* @returns Function to restore the original signal handlers
|
|
225
|
+
*/
|
|
226
|
+
trapAndForwardSignalsToChildProcess(childProcess) {
|
|
227
|
+
const signalsToTrap = ['SIGINT'];
|
|
228
|
+
const signalTraps = signalsToTrap.map(signal => {
|
|
229
|
+
process.removeAllListeners(signal);
|
|
230
|
+
const listener = () => this.kill(childProcess, signal);
|
|
231
|
+
process.on(signal, listener);
|
|
232
|
+
return [signal, listener];
|
|
233
|
+
});
|
|
234
|
+
// restores the built-in node ctrl+c and other handlers
|
|
235
|
+
return () => {
|
|
236
|
+
for (const [signal, listener] of signalTraps) {
|
|
237
|
+
process.removeListener(signal, listener);
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Waits for the psql process to exit and handles any errors.
|
|
243
|
+
*
|
|
244
|
+
* @param psql - The psql process event emitter
|
|
245
|
+
* @throws Error if psql exits with non-zero code or if psql command is not found
|
|
246
|
+
* @returns Promise that resolves to void when psql exits
|
|
247
|
+
*/
|
|
248
|
+
async waitForPSQLExit(psql) {
|
|
249
|
+
let errorToThrow = null;
|
|
250
|
+
try {
|
|
251
|
+
const [exitCode] = await once(psql, 'close');
|
|
252
|
+
pgDebug(`psql exited with code ${exitCode}`);
|
|
253
|
+
if (exitCode > 0) {
|
|
254
|
+
errorToThrow = new Error(`psql exited with code ${exitCode}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
pgDebug('psql process error', error);
|
|
259
|
+
const { code } = error;
|
|
260
|
+
if (code === 'ENOENT') {
|
|
261
|
+
errorToThrow = new Error('The local psql command could not be located. For help installing psql, see '
|
|
262
|
+
+ 'https://devcenter.heroku.com/articles/heroku-postgresql#local-setup');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (errorToThrow) {
|
|
266
|
+
throw errorToThrow;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
package/dist/ux/table.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ type Columns<T extends Record<string, unknown>> = {
|
|
|
10
10
|
};
|
|
11
11
|
export declare function table<T extends Record<string, unknown>>(data: T[], columns: Columns<T>, options?: {
|
|
12
12
|
printLine?(s: unknown): void;
|
|
13
|
-
} & Omit<TableOptions<T>, 'data'>): void;
|
|
13
|
+
} & Omit<TableOptions<T>, 'columns' | 'data'>): void;
|
|
14
14
|
export {};
|
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
|
+
"version": "10.1.0",
|
|
5
5
|
"description": "Set of helpful CLI utilities",
|
|
6
6
|
"author": "Heroku",
|
|
7
7
|
"license": "ISC",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"@heroku-cli/test-utils": "0.1.1",
|
|
15
14
|
"@heroku-cli/schema": "^2.0.0",
|
|
15
|
+
"@heroku-cli/test-utils": "0.1.1",
|
|
16
16
|
"@types/chai": "^4.3.13",
|
|
17
17
|
"@types/chai-as-promised": "^8.0.2",
|
|
18
18
|
"@types/debug": "^4.1.12",
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
"@types/mocha": "^10.0.10",
|
|
21
21
|
"@types/node": "^22.15.3",
|
|
22
22
|
"@types/sinon": "^17.0.4",
|
|
23
|
+
"@types/sinon-chai": "^4.0.0",
|
|
23
24
|
"@types/tunnel-ssh": "4.1.1",
|
|
25
|
+
"c8": "^7.7.0",
|
|
24
26
|
"chai": "^4.4.1",
|
|
25
27
|
"chai-as-promised": "^8.0.1",
|
|
26
28
|
"eslint": "^8.57.0",
|
|
@@ -31,8 +33,8 @@
|
|
|
31
33
|
"mocha": "^10.8.2",
|
|
32
34
|
"mock-stdin": "^1.0.0",
|
|
33
35
|
"nock": "^13.2.9",
|
|
34
|
-
"nyc": "^17.1.0",
|
|
35
36
|
"sinon": "^18.0.1",
|
|
37
|
+
"sinon-chai": "^3.7.0",
|
|
36
38
|
"strip-ansi": "^6",
|
|
37
39
|
"ts-node": "^10.9.2",
|
|
38
40
|
"tsconfig-paths": "^4.2.0",
|
|
@@ -58,7 +60,7 @@
|
|
|
58
60
|
"example": "sh examples/run.sh",
|
|
59
61
|
"lint": "eslint . --ext .ts --config .eslintrc.cjs",
|
|
60
62
|
"prepare": "npm run build",
|
|
61
|
-
"test": "
|
|
62
|
-
"test:local": "
|
|
63
|
+
"test": "c8 mocha --forbid-only \"test/**/*.test.ts\"",
|
|
64
|
+
"test:local": "c8 mocha \"${npm_config_file:-test/**/*.test.+(ts|tsx)}\""
|
|
63
65
|
}
|
|
64
66
|
}
|
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
}
|