@versatiles/release-tool 2.5.0 → 2.7.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 +44 -30
- package/dist/commands/check.d.ts +24 -0
- package/dist/commands/check.js +27 -3
- package/dist/commands/deps-graph.d.ts +22 -0
- package/dist/commands/deps-graph.js +23 -1
- package/dist/commands/doc-command.js +6 -6
- package/dist/commands/doc-typescript.d.ts +39 -3
- package/dist/commands/doc-typescript.js +25 -5
- package/dist/commands/markdown.d.ts +9 -1
- package/dist/commands/markdown.js +167 -38
- package/dist/commands/release-npm.d.ts +44 -1
- package/dist/commands/release-npm.js +195 -49
- package/dist/index.js +28 -13
- package/dist/lib/benchmark.d.ts +119 -0
- package/dist/lib/benchmark.js +148 -0
- package/dist/lib/changelog.d.ts +23 -0
- package/dist/lib/changelog.js +117 -0
- package/dist/lib/errors.d.ts +32 -0
- package/dist/lib/errors.js +47 -0
- package/dist/lib/git.d.ts +92 -0
- package/dist/lib/git.js +120 -8
- package/dist/lib/log.d.ts +61 -1
- package/dist/lib/log.js +76 -3
- package/dist/lib/retry.d.ts +24 -0
- package/dist/lib/retry.js +44 -0
- package/dist/lib/shell.d.ts +131 -10
- package/dist/lib/shell.js +142 -28
- package/dist/lib/utils.d.ts +29 -0
- package/dist/lib/utils.js +43 -2
- package/package.json +27 -14
package/dist/lib/log.d.ts
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enables or disables verbose logging mode.
|
|
3
|
+
* When enabled, debug messages will be printed to stderr.
|
|
4
|
+
*
|
|
5
|
+
* @param enabled - Whether to enable verbose mode.
|
|
6
|
+
*/
|
|
7
|
+
export declare function setVerbose(enabled: boolean): void;
|
|
8
|
+
/**
|
|
9
|
+
* Checks if verbose mode is currently enabled.
|
|
10
|
+
*
|
|
11
|
+
* @returns `true` if verbose mode is enabled, `false` otherwise.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isVerbose(): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Logs a fatal error message and terminates the process.
|
|
16
|
+
* The message is displayed in bold red text.
|
|
17
|
+
*
|
|
18
|
+
* @param text - The error message to display.
|
|
19
|
+
* @returns Never returns as the process is terminated.
|
|
20
|
+
*/
|
|
1
21
|
export declare function panic(text: string): never;
|
|
22
|
+
/**
|
|
23
|
+
* Logs a warning message to stderr.
|
|
24
|
+
* The message is displayed in bold yellow text.
|
|
25
|
+
*
|
|
26
|
+
* @param text - The warning message to display.
|
|
27
|
+
*/
|
|
2
28
|
export declare function warn(text: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Logs an informational message to stderr.
|
|
31
|
+
*
|
|
32
|
+
* @param text - The informational message to display.
|
|
33
|
+
*/
|
|
3
34
|
export declare function info(text: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Logs a debug message to stderr if verbose mode is enabled.
|
|
37
|
+
* The message is displayed in gray text.
|
|
38
|
+
*
|
|
39
|
+
* @param text - The debug message to display.
|
|
40
|
+
*/
|
|
41
|
+
export declare function debug(text: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Logs an abort message and terminates the process with exit code 1.
|
|
44
|
+
*
|
|
45
|
+
* @returns Never returns as the process is terminated.
|
|
46
|
+
*/
|
|
4
47
|
export declare function abort(): never;
|
|
5
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Executes an async operation with progress indication.
|
|
50
|
+
* Displays a spinner-like message while the operation is in progress,
|
|
51
|
+
* then shows a success checkmark or failure X based on the result.
|
|
52
|
+
*
|
|
53
|
+
* @typeParam T - The return type of the promise.
|
|
54
|
+
* @param message - The message to display while the operation is running.
|
|
55
|
+
* @param promise - The promise to await, or a function that returns a promise.
|
|
56
|
+
* @returns The resolved value of the promise.
|
|
57
|
+
* @throws Calls `panic()` if the promise rejects, terminating the process.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* const result = await check('Fetching data', fetchData());
|
|
62
|
+
* const result = await check('Processing', async () => processData());
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function check<T>(message: string, promise: Promise<T> | (() => Promise<T>)): Promise<T>;
|
package/dist/lib/log.js
CHANGED
|
@@ -1,17 +1,88 @@
|
|
|
1
|
+
import { VrtError } from './errors.js';
|
|
2
|
+
/** Internal flag to track verbose mode state. */
|
|
3
|
+
let verboseMode = false;
|
|
4
|
+
/**
|
|
5
|
+
* Enables or disables verbose logging mode.
|
|
6
|
+
* When enabled, debug messages will be printed to stderr.
|
|
7
|
+
*
|
|
8
|
+
* @param enabled - Whether to enable verbose mode.
|
|
9
|
+
*/
|
|
10
|
+
export function setVerbose(enabled) {
|
|
11
|
+
verboseMode = enabled;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Checks if verbose mode is currently enabled.
|
|
15
|
+
*
|
|
16
|
+
* @returns `true` if verbose mode is enabled, `false` otherwise.
|
|
17
|
+
*/
|
|
18
|
+
export function isVerbose() {
|
|
19
|
+
return verboseMode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Logs a fatal error message and terminates the process.
|
|
23
|
+
* The message is displayed in bold red text.
|
|
24
|
+
*
|
|
25
|
+
* @param text - The error message to display.
|
|
26
|
+
* @returns Never returns as the process is terminated.
|
|
27
|
+
*/
|
|
1
28
|
export function panic(text) {
|
|
2
29
|
process.stderr.write(`\x1b[1;31m! ERROR: ${text}\x1b[0m\n`);
|
|
3
30
|
abort();
|
|
4
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Logs a warning message to stderr.
|
|
34
|
+
* The message is displayed in bold yellow text.
|
|
35
|
+
*
|
|
36
|
+
* @param text - The warning message to display.
|
|
37
|
+
*/
|
|
5
38
|
export function warn(text) {
|
|
6
39
|
process.stderr.write(`\x1b[1;33m! warning: ${text}\x1b[0m\n`);
|
|
7
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Logs an informational message to stderr.
|
|
43
|
+
*
|
|
44
|
+
* @param text - The informational message to display.
|
|
45
|
+
*/
|
|
8
46
|
export function info(text) {
|
|
9
47
|
process.stderr.write(`\x1b[0mi ${text}\n`);
|
|
10
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Logs a debug message to stderr if verbose mode is enabled.
|
|
51
|
+
* The message is displayed in gray text.
|
|
52
|
+
*
|
|
53
|
+
* @param text - The debug message to display.
|
|
54
|
+
*/
|
|
55
|
+
export function debug(text) {
|
|
56
|
+
if (verboseMode) {
|
|
57
|
+
process.stderr.write(`\x1b[0;90m ${text}\x1b[0m\n`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Logs an abort message and terminates the process with exit code 1.
|
|
62
|
+
*
|
|
63
|
+
* @returns Never returns as the process is terminated.
|
|
64
|
+
*/
|
|
11
65
|
export function abort() {
|
|
12
66
|
info('abort');
|
|
13
|
-
process.exit();
|
|
67
|
+
process.exit(1);
|
|
14
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Executes an async operation with progress indication.
|
|
71
|
+
* Displays a spinner-like message while the operation is in progress,
|
|
72
|
+
* then shows a success checkmark or failure X based on the result.
|
|
73
|
+
*
|
|
74
|
+
* @typeParam T - The return type of the promise.
|
|
75
|
+
* @param message - The message to display while the operation is running.
|
|
76
|
+
* @param promise - The promise to await, or a function that returns a promise.
|
|
77
|
+
* @returns The resolved value of the promise.
|
|
78
|
+
* @throws Calls `panic()` if the promise rejects, terminating the process.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* const result = await check('Fetching data', fetchData());
|
|
83
|
+
* const result = await check('Processing', async () => processData());
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
15
86
|
export async function check(message, promise) {
|
|
16
87
|
process.stderr.write(`\x1b[0;90m\u2B95 ${message}\x1b[0m`);
|
|
17
88
|
try {
|
|
@@ -21,8 +92,10 @@ export async function check(message, promise) {
|
|
|
21
92
|
}
|
|
22
93
|
catch (error) {
|
|
23
94
|
process.stderr.write(`\r\x1b[0;91m\u2718 ${message}\x1b[0m\n`);
|
|
24
|
-
|
|
25
|
-
|
|
95
|
+
if (error instanceof VrtError) {
|
|
96
|
+
panic(`[${error.code}] ${error.message}`);
|
|
97
|
+
}
|
|
98
|
+
panic(error.message ?? String(error));
|
|
26
99
|
}
|
|
27
100
|
}
|
|
28
101
|
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for retry behavior
|
|
3
|
+
*/
|
|
4
|
+
export interface RetryOptions {
|
|
5
|
+
/** Maximum number of retry attempts (default: 3) */
|
|
6
|
+
maxRetries?: number;
|
|
7
|
+
/** Initial delay in milliseconds before first retry (default: 1000) */
|
|
8
|
+
initialDelayMs?: number;
|
|
9
|
+
/** Multiplier for exponential backoff (default: 2) */
|
|
10
|
+
backoffMultiplier?: number;
|
|
11
|
+
/** Maximum delay in milliseconds (default: 30000) */
|
|
12
|
+
maxDelayMs?: number;
|
|
13
|
+
/** Optional callback called before each retry */
|
|
14
|
+
onRetry?: (attempt: number, error: Error, nextDelayMs: number) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Executes an async function with retry logic and exponential backoff.
|
|
18
|
+
*
|
|
19
|
+
* @param fn - The async function to execute
|
|
20
|
+
* @param options - Retry configuration options
|
|
21
|
+
* @returns The result of the function if successful
|
|
22
|
+
* @throws The last error encountered after all retries are exhausted
|
|
23
|
+
*/
|
|
24
|
+
export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const DEFAULT_OPTIONS = {
|
|
2
|
+
maxRetries: 3,
|
|
3
|
+
initialDelayMs: 1000,
|
|
4
|
+
backoffMultiplier: 2,
|
|
5
|
+
maxDelayMs: 30000,
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Executes an async function with retry logic and exponential backoff.
|
|
9
|
+
*
|
|
10
|
+
* @param fn - The async function to execute
|
|
11
|
+
* @param options - Retry configuration options
|
|
12
|
+
* @returns The result of the function if successful
|
|
13
|
+
* @throws The last error encountered after all retries are exhausted
|
|
14
|
+
*/
|
|
15
|
+
export async function withRetry(fn, options = {}) {
|
|
16
|
+
const { maxRetries, initialDelayMs, backoffMultiplier, maxDelayMs } = { ...DEFAULT_OPTIONS, ...options };
|
|
17
|
+
const { onRetry } = options;
|
|
18
|
+
let lastError;
|
|
19
|
+
let currentDelay = initialDelayMs;
|
|
20
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
21
|
+
try {
|
|
22
|
+
return await fn();
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
26
|
+
if (attempt < maxRetries) {
|
|
27
|
+
const nextDelay = Math.min(currentDelay, maxDelayMs);
|
|
28
|
+
if (onRetry) {
|
|
29
|
+
onRetry(attempt + 1, lastError, nextDelay);
|
|
30
|
+
}
|
|
31
|
+
await sleep(nextDelay);
|
|
32
|
+
currentDelay *= backoffMultiplier;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
throw lastError;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Sleep for a specified number of milliseconds
|
|
40
|
+
*/
|
|
41
|
+
function sleep(ms) {
|
|
42
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=retry.js.map
|
package/dist/lib/shell.d.ts
CHANGED
|
@@ -1,17 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of a shell command execution.
|
|
3
|
+
*/
|
|
4
|
+
export interface ShellResult {
|
|
5
|
+
/** Exit code of the process, or null if terminated by signal. */
|
|
6
|
+
code: number | null;
|
|
7
|
+
/** Signal that terminated the process, or null if exited normally. */
|
|
8
|
+
signal: string | null;
|
|
9
|
+
/** Captured standard output. */
|
|
10
|
+
stdout: string;
|
|
11
|
+
/** Captured standard error. */
|
|
12
|
+
stderr: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Result of an interactive shell command execution.
|
|
16
|
+
*/
|
|
17
|
+
export interface ShellInteractiveResult {
|
|
18
|
+
/** Exit code of the process, or null if terminated by signal. */
|
|
19
|
+
code: number | null;
|
|
20
|
+
/** Signal that terminated the process, or null if exited normally. */
|
|
21
|
+
signal: string | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A utility class for executing shell commands in a specified working directory.
|
|
25
|
+
* Provides methods for running commands with captured output, interactive commands,
|
|
26
|
+
* and convenience methods for common patterns.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const shell = new Shell('/path/to/project');
|
|
31
|
+
* const output = await shell.stdout('git status');
|
|
32
|
+
* const success = await shell.ok('npm test');
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
1
35
|
export declare class Shell {
|
|
36
|
+
/** The working directory for all commands. */
|
|
2
37
|
private cwd;
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new Shell instance.
|
|
40
|
+
*
|
|
41
|
+
* @param cwd - The working directory for executing commands.
|
|
42
|
+
*/
|
|
3
43
|
constructor(cwd: string);
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Runs a shell command through bash and captures its output.
|
|
46
|
+
*
|
|
47
|
+
* @remarks
|
|
48
|
+
* **⚠️ SECURITY WARNING: Command Injection Risk**
|
|
49
|
+
*
|
|
50
|
+
* This method passes the command string directly to `bash -c`, which means
|
|
51
|
+
* shell metacharacters (`;`, `|`, `$()`, `` ` ``, etc.) are interpreted.
|
|
52
|
+
*
|
|
53
|
+
* **NEVER** pass unsanitized user input to this method:
|
|
54
|
+
* ```ts
|
|
55
|
+
* // DANGEROUS - command injection vulnerability!
|
|
56
|
+
* shell.run(`git checkout ${userInput}`); // userInput could be "; rm -rf /"
|
|
57
|
+
*
|
|
58
|
+
* // SAFE - use exec() with array arguments instead
|
|
59
|
+
* shell.exec('git', ['checkout', userInput]);
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* Only use this method with:
|
|
63
|
+
* - Hardcoded command strings
|
|
64
|
+
* - Values that have been strictly validated (e.g., version numbers matching `/^\d+\.\d+\.\d+$/`)
|
|
65
|
+
*
|
|
66
|
+
* For commands with dynamic arguments, prefer {@link Shell.exec} which passes
|
|
67
|
+
* arguments directly without shell interpretation.
|
|
68
|
+
*
|
|
69
|
+
* @param command - The shell command to execute. Must be a trusted string.
|
|
70
|
+
* @param errorOnCodeNonZero - If true (default), rejects the promise on non-zero exit code.
|
|
71
|
+
* @returns A promise resolving to the command result with exit code, signal, stdout, and stderr.
|
|
72
|
+
* @throws Rejects with the result object if errorOnCodeNonZero is true and exit code is non-zero.
|
|
73
|
+
*/
|
|
74
|
+
run(command: string, errorOnCodeNonZero?: boolean): Promise<ShellResult>;
|
|
75
|
+
/**
|
|
76
|
+
* Executes a command with arguments directly, avoiding shell escaping issues.
|
|
77
|
+
* Preferred over `run()` when dealing with arguments that may contain special characters.
|
|
78
|
+
*
|
|
79
|
+
* @param command - The command executable to run.
|
|
80
|
+
* @param args - Array of arguments to pass to the command.
|
|
81
|
+
* @param errorOnCodeNonZero - If true (default), rejects the promise on non-zero exit code.
|
|
82
|
+
* @param skipLog - If true, suppresses debug logging of the command.
|
|
83
|
+
* @returns A promise resolving to the command result with exit code, signal, stdout, and stderr.
|
|
84
|
+
* @throws Rejects with the result object if errorOnCodeNonZero is true and exit code is non-zero.
|
|
85
|
+
*/
|
|
86
|
+
exec(command: string, args: string[], errorOnCodeNonZero?: boolean, skipLog?: boolean): Promise<ShellResult>;
|
|
87
|
+
/**
|
|
88
|
+
* Runs a command interactively with full TTY passthrough.
|
|
89
|
+
* The user can interact with the command's stdin/stdout/stderr directly.
|
|
90
|
+
* Useful for commands that require user input (e.g., npm publish with OTP).
|
|
91
|
+
*
|
|
92
|
+
* @remarks
|
|
93
|
+
* **⚠️ SECURITY WARNING:** Same command injection risks as {@link Shell.run}.
|
|
94
|
+
* Never pass unsanitized user input. See {@link Shell.run} for details.
|
|
95
|
+
*
|
|
96
|
+
* @param command - The shell command to execute. Must be a trusted string.
|
|
97
|
+
* @param errorOnCodeNonZero - If true (default), rejects the promise on non-zero exit code.
|
|
98
|
+
* @returns A promise resolving to the exit code and signal (no captured output).
|
|
99
|
+
* @throws Rejects with the result object if errorOnCodeNonZero is true and exit code is non-zero.
|
|
100
|
+
*/
|
|
101
|
+
runInteractive(command: string, errorOnCodeNonZero?: boolean): Promise<ShellInteractiveResult>;
|
|
102
|
+
/**
|
|
103
|
+
* Runs a command and returns only the trimmed stderr output.
|
|
104
|
+
*
|
|
105
|
+
* @remarks
|
|
106
|
+
* **⚠️ SECURITY WARNING:** Uses {@link Shell.run} internally.
|
|
107
|
+
* Never pass unsanitized user input. See {@link Shell.run} for details.
|
|
108
|
+
*
|
|
109
|
+
* @param command - The shell command to execute. Must be a trusted string.
|
|
110
|
+
* @param errorOnCodeZero - If true (default), rejects on non-zero exit code.
|
|
111
|
+
* @returns A promise resolving to the trimmed stderr string.
|
|
112
|
+
*/
|
|
14
113
|
stderr(command: string, errorOnCodeZero?: boolean): Promise<string>;
|
|
114
|
+
/**
|
|
115
|
+
* Runs a command and returns only the trimmed stdout output.
|
|
116
|
+
*
|
|
117
|
+
* @remarks
|
|
118
|
+
* **⚠️ SECURITY WARNING:** Uses {@link Shell.run} internally.
|
|
119
|
+
* Never pass unsanitized user input. See {@link Shell.run} for details.
|
|
120
|
+
*
|
|
121
|
+
* @param command - The shell command to execute. Must be a trusted string.
|
|
122
|
+
* @param errorOnCodeZero - If true (default), rejects on non-zero exit code.
|
|
123
|
+
* @returns A promise resolving to the trimmed stdout string.
|
|
124
|
+
*/
|
|
15
125
|
stdout(command: string, errorOnCodeZero?: boolean): Promise<string>;
|
|
126
|
+
/**
|
|
127
|
+
* Runs a command and returns whether it succeeded (exit code 0).
|
|
128
|
+
* Never throws on non-zero exit codes.
|
|
129
|
+
*
|
|
130
|
+
* @remarks
|
|
131
|
+
* **⚠️ SECURITY WARNING:** Uses {@link Shell.run} internally.
|
|
132
|
+
* Never pass unsanitized user input. See {@link Shell.run} for details.
|
|
133
|
+
*
|
|
134
|
+
* @param command - The shell command to execute. Must be a trusted string.
|
|
135
|
+
* @returns A promise resolving to true if exit code is 0, false otherwise.
|
|
136
|
+
*/
|
|
16
137
|
ok(command: string): Promise<boolean>;
|
|
17
138
|
}
|
package/dist/lib/shell.js
CHANGED
|
@@ -1,40 +1,121 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
+
import { debug, isVerbose } from './log.js';
|
|
3
|
+
/**
|
|
4
|
+
* A utility class for executing shell commands in a specified working directory.
|
|
5
|
+
* Provides methods for running commands with captured output, interactive commands,
|
|
6
|
+
* and convenience methods for common patterns.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const shell = new Shell('/path/to/project');
|
|
11
|
+
* const output = await shell.stdout('git status');
|
|
12
|
+
* const success = await shell.ok('npm test');
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
2
15
|
export class Shell {
|
|
16
|
+
/** The working directory for all commands. */
|
|
3
17
|
cwd;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new Shell instance.
|
|
20
|
+
*
|
|
21
|
+
* @param cwd - The working directory for executing commands.
|
|
22
|
+
*/
|
|
4
23
|
constructor(cwd) {
|
|
5
24
|
this.cwd = cwd;
|
|
6
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Runs a shell command through bash and captures its output.
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* **⚠️ SECURITY WARNING: Command Injection Risk**
|
|
31
|
+
*
|
|
32
|
+
* This method passes the command string directly to `bash -c`, which means
|
|
33
|
+
* shell metacharacters (`;`, `|`, `$()`, `` ` ``, etc.) are interpreted.
|
|
34
|
+
*
|
|
35
|
+
* **NEVER** pass unsanitized user input to this method:
|
|
36
|
+
* ```ts
|
|
37
|
+
* // DANGEROUS - command injection vulnerability!
|
|
38
|
+
* shell.run(`git checkout ${userInput}`); // userInput could be "; rm -rf /"
|
|
39
|
+
*
|
|
40
|
+
* // SAFE - use exec() with array arguments instead
|
|
41
|
+
* shell.exec('git', ['checkout', userInput]);
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* Only use this method with:
|
|
45
|
+
* - Hardcoded command strings
|
|
46
|
+
* - Values that have been strictly validated (e.g., version numbers matching `/^\d+\.\d+\.\d+$/`)
|
|
47
|
+
*
|
|
48
|
+
* For commands with dynamic arguments, prefer {@link Shell.exec} which passes
|
|
49
|
+
* arguments directly without shell interpretation.
|
|
50
|
+
*
|
|
51
|
+
* @param command - The shell command to execute. Must be a trusted string.
|
|
52
|
+
* @param errorOnCodeNonZero - If true (default), rejects the promise on non-zero exit code.
|
|
53
|
+
* @returns A promise resolving to the command result with exit code, signal, stdout, and stderr.
|
|
54
|
+
* @throws Rejects with the result object if errorOnCodeNonZero is true and exit code is non-zero.
|
|
55
|
+
*/
|
|
7
56
|
async run(command, errorOnCodeNonZero = true) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
resolve(result);
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
cp.stdout.on('data', chunk => stdout.push(chunk));
|
|
29
|
-
cp.stderr.on('data', chunk => stderr.push(chunk));
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
console.error(error);
|
|
34
|
-
throw error;
|
|
57
|
+
debug(`$ ${command}`);
|
|
58
|
+
return this.exec('bash', ['-c', command], errorOnCodeNonZero, true);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Executes a command with arguments directly, avoiding shell escaping issues.
|
|
62
|
+
* Preferred over `run()` when dealing with arguments that may contain special characters.
|
|
63
|
+
*
|
|
64
|
+
* @param command - The command executable to run.
|
|
65
|
+
* @param args - Array of arguments to pass to the command.
|
|
66
|
+
* @param errorOnCodeNonZero - If true (default), rejects the promise on non-zero exit code.
|
|
67
|
+
* @param skipLog - If true, suppresses debug logging of the command.
|
|
68
|
+
* @returns A promise resolving to the command result with exit code, signal, stdout, and stderr.
|
|
69
|
+
* @throws Rejects with the result object if errorOnCodeNonZero is true and exit code is non-zero.
|
|
70
|
+
*/
|
|
71
|
+
async exec(command, args, errorOnCodeNonZero = true, skipLog = false) {
|
|
72
|
+
if (!skipLog) {
|
|
73
|
+
debug(`$ ${command} ${args.join(' ')}`);
|
|
35
74
|
}
|
|
75
|
+
return await new Promise((resolve, reject) => {
|
|
76
|
+
const stdout = [];
|
|
77
|
+
const stderr = [];
|
|
78
|
+
const cp = spawn(command, args, { cwd: this.cwd })
|
|
79
|
+
.on('error', (error) => reject(error))
|
|
80
|
+
.on('close', (code, signal) => {
|
|
81
|
+
const result = {
|
|
82
|
+
code,
|
|
83
|
+
signal,
|
|
84
|
+
stdout: Buffer.concat(stdout).toString(),
|
|
85
|
+
stderr: Buffer.concat(stderr).toString(),
|
|
86
|
+
};
|
|
87
|
+
if (isVerbose()) {
|
|
88
|
+
if (result.stdout)
|
|
89
|
+
result.stdout.split('\n').forEach((line) => debug(` stdout: ${line}`));
|
|
90
|
+
if (result.stderr)
|
|
91
|
+
result.stderr.split('\n').forEach((line) => debug(` stderr: ${line}`));
|
|
92
|
+
debug(` exit code: ${code}`);
|
|
93
|
+
}
|
|
94
|
+
if (errorOnCodeNonZero && code !== 0) {
|
|
95
|
+
reject(result);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
resolve(result);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
cp.stdout.on('data', (chunk) => stdout.push(chunk));
|
|
102
|
+
cp.stderr.on('data', (chunk) => stderr.push(chunk));
|
|
103
|
+
});
|
|
36
104
|
}
|
|
37
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Runs a command interactively with full TTY passthrough.
|
|
107
|
+
* The user can interact with the command's stdin/stdout/stderr directly.
|
|
108
|
+
* Useful for commands that require user input (e.g., npm publish with OTP).
|
|
109
|
+
*
|
|
110
|
+
* @remarks
|
|
111
|
+
* **⚠️ SECURITY WARNING:** Same command injection risks as {@link Shell.run}.
|
|
112
|
+
* Never pass unsanitized user input. See {@link Shell.run} for details.
|
|
113
|
+
*
|
|
114
|
+
* @param command - The shell command to execute. Must be a trusted string.
|
|
115
|
+
* @param errorOnCodeNonZero - If true (default), rejects the promise on non-zero exit code.
|
|
116
|
+
* @returns A promise resolving to the exit code and signal (no captured output).
|
|
117
|
+
* @throws Rejects with the result object if errorOnCodeNonZero is true and exit code is non-zero.
|
|
118
|
+
*/
|
|
38
119
|
async runInteractive(command, errorOnCodeNonZero = true) {
|
|
39
120
|
return await new Promise((resolve, reject) => {
|
|
40
121
|
const cp = spawn('bash', ['-c', command], {
|
|
@@ -53,14 +134,47 @@ export class Shell {
|
|
|
53
134
|
});
|
|
54
135
|
});
|
|
55
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Runs a command and returns only the trimmed stderr output.
|
|
139
|
+
*
|
|
140
|
+
* @remarks
|
|
141
|
+
* **⚠️ SECURITY WARNING:** Uses {@link Shell.run} internally.
|
|
142
|
+
* Never pass unsanitized user input. See {@link Shell.run} for details.
|
|
143
|
+
*
|
|
144
|
+
* @param command - The shell command to execute. Must be a trusted string.
|
|
145
|
+
* @param errorOnCodeZero - If true (default), rejects on non-zero exit code.
|
|
146
|
+
* @returns A promise resolving to the trimmed stderr string.
|
|
147
|
+
*/
|
|
56
148
|
async stderr(command, errorOnCodeZero) {
|
|
57
149
|
const result = await this.run(command, errorOnCodeZero);
|
|
58
150
|
return result.stderr.trim();
|
|
59
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Runs a command and returns only the trimmed stdout output.
|
|
154
|
+
*
|
|
155
|
+
* @remarks
|
|
156
|
+
* **⚠️ SECURITY WARNING:** Uses {@link Shell.run} internally.
|
|
157
|
+
* Never pass unsanitized user input. See {@link Shell.run} for details.
|
|
158
|
+
*
|
|
159
|
+
* @param command - The shell command to execute. Must be a trusted string.
|
|
160
|
+
* @param errorOnCodeZero - If true (default), rejects on non-zero exit code.
|
|
161
|
+
* @returns A promise resolving to the trimmed stdout string.
|
|
162
|
+
*/
|
|
60
163
|
async stdout(command, errorOnCodeZero) {
|
|
61
164
|
const result = await this.run(command, errorOnCodeZero);
|
|
62
165
|
return result.stdout.trim();
|
|
63
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Runs a command and returns whether it succeeded (exit code 0).
|
|
169
|
+
* Never throws on non-zero exit codes.
|
|
170
|
+
*
|
|
171
|
+
* @remarks
|
|
172
|
+
* **⚠️ SECURITY WARNING:** Uses {@link Shell.run} internally.
|
|
173
|
+
* Never pass unsanitized user input. See {@link Shell.run} for details.
|
|
174
|
+
*
|
|
175
|
+
* @param command - The shell command to execute. Must be a trusted string.
|
|
176
|
+
* @returns A promise resolving to true if exit code is 0, false otherwise.
|
|
177
|
+
*/
|
|
64
178
|
async ok(command) {
|
|
65
179
|
const result = await this.run(command, false);
|
|
66
180
|
return result.code === 0;
|
package/dist/lib/utils.d.ts
CHANGED
|
@@ -1,2 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts a human-readable error message from an unknown error value.
|
|
3
|
+
* Safely handles various error formats including Error objects, objects with message properties,
|
|
4
|
+
* and other unknown values.
|
|
5
|
+
*
|
|
6
|
+
* @param error - The error value to extract a message from. Can be any type.
|
|
7
|
+
* @returns The extracted error message string, or 'unknown' if no message could be extracted.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* getErrorMessage(new Error('Something went wrong')); // 'Something went wrong'
|
|
12
|
+
* getErrorMessage({ message: 'Custom error' }); // 'Custom error'
|
|
13
|
+
* getErrorMessage(null); // 'unknown'
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
1
16
|
export declare function getErrorMessage(error: unknown): string;
|
|
17
|
+
/**
|
|
18
|
+
* Formats JSON data with custom styling rules for map-style configurations.
|
|
19
|
+
* Applies special formatting for certain paths (like bounds, layers, filters, paint, layout)
|
|
20
|
+
* to keep them on a single line while expanding other nested structures.
|
|
21
|
+
*
|
|
22
|
+
* @param inputData - The data to format as styled JSON.
|
|
23
|
+
* @returns A formatted JSON string with custom indentation and line breaks.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* prettyStyleJSON({ name: 'test', bounds: [0, 0, 1, 1] });
|
|
28
|
+
* // Returns formatted JSON with bounds on a single line
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
2
31
|
export declare function prettyStyleJSON(inputData: unknown): string;
|
package/dist/lib/utils.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts a human-readable error message from an unknown error value.
|
|
3
|
+
* Safely handles various error formats including Error objects, objects with message properties,
|
|
4
|
+
* and other unknown values.
|
|
5
|
+
*
|
|
6
|
+
* @param error - The error value to extract a message from. Can be any type.
|
|
7
|
+
* @returns The extracted error message string, or 'unknown' if no message could be extracted.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* getErrorMessage(new Error('Something went wrong')); // 'Something went wrong'
|
|
12
|
+
* getErrorMessage({ message: 'Custom error' }); // 'Custom error'
|
|
13
|
+
* getErrorMessage(null); // 'unknown'
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
1
16
|
export function getErrorMessage(error) {
|
|
2
17
|
if (error == null)
|
|
3
18
|
return 'unknown';
|
|
@@ -10,6 +25,20 @@ export function getErrorMessage(error) {
|
|
|
10
25
|
}
|
|
11
26
|
return 'unknown';
|
|
12
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Formats JSON data with custom styling rules for map-style configurations.
|
|
30
|
+
* Applies special formatting for certain paths (like bounds, layers, filters, paint, layout)
|
|
31
|
+
* to keep them on a single line while expanding other nested structures.
|
|
32
|
+
*
|
|
33
|
+
* @param inputData - The data to format as styled JSON.
|
|
34
|
+
* @returns A formatted JSON string with custom indentation and line breaks.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* prettyStyleJSON({ name: 'test', bounds: [0, 0, 1, 1] });
|
|
39
|
+
* // Returns formatted JSON with bounds on a single line
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
13
42
|
export function prettyStyleJSON(inputData) {
|
|
14
43
|
return recursive(inputData);
|
|
15
44
|
function recursive(data, prefix = '', path = '') {
|
|
@@ -24,10 +53,22 @@ export function prettyStyleJSON(inputData) {
|
|
|
24
53
|
return singleLine(data);
|
|
25
54
|
if (typeof data === 'object') {
|
|
26
55
|
if (Array.isArray(data)) {
|
|
27
|
-
return '[\n\t' +
|
|
56
|
+
return ('[\n\t' +
|
|
57
|
+
prefix +
|
|
58
|
+
data.map((value) => recursive(value, prefix + '\t', path + '[]')).join(',\n\t' + prefix) +
|
|
59
|
+
'\n' +
|
|
60
|
+
prefix +
|
|
61
|
+
']');
|
|
28
62
|
}
|
|
29
63
|
if (data) {
|
|
30
|
-
return '{\n\t' +
|
|
64
|
+
return ('{\n\t' +
|
|
65
|
+
prefix +
|
|
66
|
+
Object.entries(data)
|
|
67
|
+
.map(([key, value]) => '"' + key + '": ' + recursive(value, prefix + '\t', path + '.' + key))
|
|
68
|
+
.join(',\n\t' + prefix) +
|
|
69
|
+
'\n' +
|
|
70
|
+
prefix +
|
|
71
|
+
'}');
|
|
31
72
|
}
|
|
32
73
|
}
|
|
33
74
|
return singleLine(data);
|