@versatiles/release-tool 2.6.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.
@@ -1,23 +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
- run(command: string, errorOnCodeNonZero?: boolean): Promise<{
5
- code: number | null;
6
- signal: string | null;
7
- stdout: string;
8
- stderr: string;
9
- }>;
10
- exec(command: string, args: string[], errorOnCodeNonZero?: boolean, skipLog?: boolean): Promise<{
11
- code: number | null;
12
- signal: string | null;
13
- stdout: string;
14
- stderr: string;
15
- }>;
16
- runInteractive(command: string, errorOnCodeNonZero?: boolean): Promise<{
17
- code: number | null;
18
- signal: string | null;
19
- }>;
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
+ */
20
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
+ */
21
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
+ */
22
137
  ok(command: string): Promise<boolean>;
23
138
  }
package/dist/lib/shell.js CHANGED
@@ -1,15 +1,73 @@
1
1
  import { spawn } from 'child_process';
2
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
+ */
3
15
  export class Shell {
16
+ /** The working directory for all commands. */
4
17
  cwd;
18
+ /**
19
+ * Creates a new Shell instance.
20
+ *
21
+ * @param cwd - The working directory for executing commands.
22
+ */
5
23
  constructor(cwd) {
6
24
  this.cwd = cwd;
7
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
+ */
8
56
  async run(command, errorOnCodeNonZero = true) {
9
57
  debug(`$ ${command}`);
10
58
  return this.exec('bash', ['-c', command], errorOnCodeNonZero, true);
11
59
  }
12
- // Execute a command with arguments directly, avoiding shell escaping issues
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
+ */
13
71
  async exec(command, args, errorOnCodeNonZero = true, skipLog = false) {
14
72
  if (!skipLog) {
15
73
  debug(`$ ${command} ${args.join(' ')}`);
@@ -44,7 +102,20 @@ export class Shell {
44
102
  cp.stderr.on('data', (chunk) => stderr.push(chunk));
45
103
  });
46
104
  }
47
- // Runs a command interactively, so the user can interact with stdin/stdout/stderr directly.
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
+ */
48
119
  async runInteractive(command, errorOnCodeNonZero = true) {
49
120
  return await new Promise((resolve, reject) => {
50
121
  const cp = spawn('bash', ['-c', command], {
@@ -63,14 +134,47 @@ export class Shell {
63
134
  });
64
135
  });
65
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
+ */
66
148
  async stderr(command, errorOnCodeZero) {
67
149
  const result = await this.run(command, errorOnCodeZero);
68
150
  return result.stderr.trim();
69
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
+ */
70
163
  async stdout(command, errorOnCodeZero) {
71
164
  const result = await this.run(command, errorOnCodeZero);
72
165
  return result.stdout.trim();
73
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
+ */
74
178
  async ok(command) {
75
179
  const result = await this.run(command, false);
76
180
  return result.code === 0;
@@ -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 = '') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versatiles/release-tool",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "VersaTiles release and documentation tools",
5
5
  "bin": {
6
6
  "vrt": "./dist/index.js"
@@ -11,6 +11,7 @@
11
11
  ],
12
12
  "scripts": {
13
13
  "build": "npm run build-node && npm run doc",
14
+ "prepare": "husky",
14
15
  "build-node": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/index.js",
15
16
  "check": "npm run lint && npm run build && npm run test && npm run format:check",
16
17
  "dev": "tsx src/index.ts",
@@ -39,11 +40,13 @@
39
40
  "homepage": "https://github.com/versatiles-org/node-release-tool",
40
41
  "devDependencies": {
41
42
  "@schemastore/package": "^0.0.10",
42
- "@types/node": "^25.1.0",
43
+ "@types/node": "^25.2.0",
43
44
  "@typescript-eslint/eslint-plugin": "^8.54.0",
44
45
  "@typescript-eslint/parser": "^8.54.0",
45
46
  "@vitest/coverage-v8": "^4.0.18",
46
47
  "eslint": "^9.39.2",
48
+ "husky": "^9.1.7",
49
+ "lint-staged": "^16.2.7",
47
50
  "prettier": "^3.8.1",
48
51
  "tsx": "^4.21.0",
49
52
  "typescript": "^5.9.3",
@@ -52,7 +55,7 @@
52
55
  },
53
56
  "dependencies": {
54
57
  "@inquirer/select": "^5.0.4",
55
- "commander": "^14.0.2",
58
+ "commander": "^14.0.3",
56
59
  "dependency-cruiser": "^17.3.7",
57
60
  "npm-check-updates": "^19.3.2",
58
61
  "remark": "^15.0.1",
@@ -61,5 +64,9 @@
61
64
  "typedoc-github-theme": "^0.3.1",
62
65
  "typedoc-github-wiki-theme": "^2.1.0",
63
66
  "typedoc-plugin-markdown": "^4.9.0"
67
+ },
68
+ "lint-staged": {
69
+ "*.{ts,js,json,md,yml,yaml}": "prettier --write",
70
+ "*.ts": "eslint --fix"
64
71
  }
65
72
  }