@j-o-r/sh 1.0.0 → 1.0.2
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 +20 -1
- package/lib/sh.d.ts +7 -2
- package/lib/sh.js +64 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ This class is returned by the `SH` function. Here's a summary of its methods and
|
|
|
75
75
|
|
|
76
76
|
- **options(options)**: Sets options for the command execution.
|
|
77
77
|
- **run(payload?)**: Executes the command and returns a promise that resolves with the command's output.
|
|
78
|
-
- **runSync()**: Executes the command synchronously and returns a `SpawnSyncResponse`.
|
|
78
|
+
- **runSync(payload?)**: Executes the command synchronously and returns a `SpawnSyncResponse`.
|
|
79
79
|
- **kill()**: Sends a kill signal to the child process.
|
|
80
80
|
|
|
81
81
|
### Examples
|
|
@@ -113,7 +113,26 @@ This class is returned by the `SH` function. Here's a summary of its methods and
|
|
|
113
113
|
console.error('Retry failed:', e);
|
|
114
114
|
}
|
|
115
115
|
```
|
|
116
|
+
- Method for copying data to the clipboard:
|
|
117
|
+
```javascript
|
|
118
|
+
/**
|
|
119
|
+
* Copy text to the clipboard
|
|
120
|
+
* @param {string} text
|
|
121
|
+
* @retruns {Promise<string>}
|
|
122
|
+
*/
|
|
123
|
+
const copyToClipboard = async (text) => {
|
|
124
|
+
const prams = [
|
|
125
|
+
'-selection',
|
|
126
|
+
'clipboard'
|
|
127
|
+
]
|
|
128
|
+
return SH`xclip ${prams}`.options({stdio: 'inherit'}).run(text);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
116
131
|
|
|
132
|
+
- Open the 'vim' editor
|
|
133
|
+
```javascript
|
|
134
|
+
SH`vim`.options({stdio: 'inherit'}).runSync();
|
|
135
|
+
```
|
|
117
136
|
## License
|
|
118
137
|
|
|
119
138
|
This project is licensed under the Apache License, Version 2.0.
|
package/lib/sh.d.ts
CHANGED
|
@@ -47,7 +47,11 @@ export type SHOptions = {
|
|
|
47
47
|
/**
|
|
48
48
|
* - The stdio configuration.
|
|
49
49
|
*/
|
|
50
|
-
stdio?:
|
|
50
|
+
stdio?: StdioOptions | StdioOption;
|
|
51
|
+
/**
|
|
52
|
+
* - default 20000: a timeout error is triggerd when a process execution time is exceeded, 0 is no timeout
|
|
53
|
+
*/
|
|
54
|
+
timeout?: number;
|
|
51
55
|
};
|
|
52
56
|
export type StdioOption = ('pipe' | 'ignore' | 'inherit' | number);
|
|
53
57
|
export type StdioOptions = Array<StdioOption> | StdioOption;
|
|
@@ -138,9 +142,10 @@ declare class SHDispatch {
|
|
|
138
142
|
run(payload?: string): Promise<string>;
|
|
139
143
|
/**
|
|
140
144
|
* Works for screen takeovers like editors
|
|
145
|
+
* @param {string} [payload]
|
|
141
146
|
* @returns {SpawnSyncResponse}
|
|
142
147
|
*/
|
|
143
|
-
runSync(): SpawnSyncResponse;
|
|
148
|
+
runSync(payload?: string): SpawnSyncResponse;
|
|
144
149
|
kill(): Promise<void>;
|
|
145
150
|
#private;
|
|
146
151
|
}
|
package/lib/sh.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
1
2
|
import { spawnSync, spawn, exec } from 'node:child_process';
|
|
2
3
|
|
|
4
|
+
// timeout when a process becomes inresponsive
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* Kills a process and all child processes of a given process ID in Linux/Posix.
|
|
5
8
|
* @param {number} processPid - The process ID.
|
|
@@ -45,11 +48,11 @@ const killProcesses = (processPid, signal) => {
|
|
|
45
48
|
};
|
|
46
49
|
|
|
47
50
|
class SHExecute {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
#proc;
|
|
52
|
+
#command = '';
|
|
53
|
+
#options = {};
|
|
54
|
+
#stdout = '';
|
|
55
|
+
#stderr = '';
|
|
53
56
|
constructor(command, options = {}) {
|
|
54
57
|
this.#command = command;
|
|
55
58
|
this.#options = options;
|
|
@@ -57,26 +60,42 @@ class SHExecute {
|
|
|
57
60
|
this.#stdout = '';
|
|
58
61
|
this.#stderr = '';
|
|
59
62
|
}
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
/**
|
|
64
|
+
* @param {string} [payload] - data to write
|
|
65
|
+
* @retuns {Promise<object>}
|
|
66
|
+
*/
|
|
67
|
+
runSync(payload) {
|
|
68
|
+
if (payload && typeof payload !== 'string') {
|
|
69
|
+
throw new Error('Argument is not a string');
|
|
70
|
+
}
|
|
71
|
+
let { cwd, shell, env, stdio } = this.#options;
|
|
72
|
+
// pipe need to be set on stdin when posting a payload
|
|
73
|
+
if (payload) stdio[0] = 'pipe';
|
|
74
|
+
const input = payload || undefined;
|
|
62
75
|
return spawnSync(this.#options.prefix, [this.#command], {
|
|
63
76
|
cwd,
|
|
64
77
|
shell,
|
|
65
78
|
stdio,
|
|
66
79
|
windowsHide: true,
|
|
67
80
|
env,
|
|
81
|
+
input
|
|
68
82
|
});
|
|
69
83
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
/**
|
|
85
|
+
* @param {string} [payload] - data to write
|
|
86
|
+
* @retuns {Promise<string>}
|
|
87
|
+
*/
|
|
74
88
|
run(payload) {
|
|
89
|
+
let to = 0;
|
|
75
90
|
if (payload && typeof payload !== 'string') {
|
|
76
91
|
throw new Error('Argument is not a string');
|
|
77
|
-
}
|
|
92
|
+
}
|
|
93
|
+
if (this.#options.timeout) {
|
|
94
|
+
to = this.#options.timeout;
|
|
95
|
+
}
|
|
78
96
|
let { cwd, shell, env, stdio } = this.#options;
|
|
79
|
-
|
|
97
|
+
// pipe need to be set on stdin when posting a payload
|
|
98
|
+
if (payload) stdio[0] = 'pipe';
|
|
80
99
|
this.#proc = spawn(this.#options.prefix, [this.#command], {
|
|
81
100
|
cwd,
|
|
82
101
|
shell,
|
|
@@ -92,12 +111,19 @@ class SHExecute {
|
|
|
92
111
|
this.#proc.stderr?.on('data', (data) => {
|
|
93
112
|
this.#stderr += data;
|
|
94
113
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
114
|
+
if (payload) {
|
|
115
|
+
this.#proc.stdin.end(payload);
|
|
116
|
+
}
|
|
99
117
|
return new Promise((resolve, reject) => {
|
|
118
|
+
let timeout;
|
|
119
|
+
if (to > 0) {
|
|
120
|
+
timeout = setTimeout(async () => {
|
|
121
|
+
await this.#proc.kill();
|
|
122
|
+
reject(new Error(`Process timed out: ${this.#command}`));
|
|
123
|
+
}, to); // options.timeout
|
|
124
|
+
}
|
|
100
125
|
this.#proc.on('close', (code) => {
|
|
126
|
+
if (timeout) clearTimeout(timeout);
|
|
101
127
|
if (code === 0) {
|
|
102
128
|
resolve(this.#stdout.trim());
|
|
103
129
|
} else {
|
|
@@ -110,9 +136,9 @@ class SHExecute {
|
|
|
110
136
|
});
|
|
111
137
|
});
|
|
112
138
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
139
|
+
/**
|
|
140
|
+
* @returns {Promise<number[]>}
|
|
141
|
+
*/
|
|
116
142
|
async kill(signal = 'SIGTERM') {
|
|
117
143
|
if (!this.#proc) throw new Error('Trying to kill a process without creating one.');
|
|
118
144
|
if (!this.#proc.pid) throw new Error('The process pid is undefined.');
|
|
@@ -138,7 +164,8 @@ class SHExecute {
|
|
|
138
164
|
* @property {NodeJS.ProcessEnv} [env] - The environment variables.
|
|
139
165
|
* @property {string} [shell] - The shell to use for execution.
|
|
140
166
|
* @property {string} [prefix] - The prefix commands to ensure a safe execution environment.
|
|
141
|
-
* @property {StdioOption
|
|
167
|
+
* @property {StdioOptions|StdioOption} [stdio] - The stdio configuration.
|
|
168
|
+
* @property {number} [timeout] - default 20000: a timeout error is triggerd when a process execution time is exceeded, 0 is no timeout
|
|
142
169
|
*/
|
|
143
170
|
/**
|
|
144
171
|
* @typedef {('pipe' | 'ignore' | 'inherit' | number)} StdioOption
|
|
@@ -180,9 +207,9 @@ const hasProp = (o, p) => {
|
|
|
180
207
|
|
|
181
208
|
/**
|
|
182
209
|
* Merge property values while maintaining the fixed set of props in the original object
|
|
183
|
-
* @param {
|
|
184
|
-
* @param {
|
|
185
|
-
* @
|
|
210
|
+
* @param {SHOptions} predefined - original object
|
|
211
|
+
* @param {SHOptions} options - object with new values
|
|
212
|
+
* @returns {SHOptions}
|
|
186
213
|
*/
|
|
187
214
|
const mergeOptions = (predefined, options) => {
|
|
188
215
|
// Extract the keys from the predefined object
|
|
@@ -208,7 +235,8 @@ const defaultOptions = {
|
|
|
208
235
|
env: process.env,
|
|
209
236
|
shell: 'bash',
|
|
210
237
|
prefix: 'set -euo pipefail;/usr/bin/env',
|
|
211
|
-
stdio: ['inherit', 'pipe', 'pipe']
|
|
238
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
239
|
+
timeout: 10000 // when 0 there is no timeout
|
|
212
240
|
};
|
|
213
241
|
|
|
214
242
|
|
|
@@ -229,6 +257,13 @@ class SHDispatch {
|
|
|
229
257
|
* @returns {SHDispatch}
|
|
230
258
|
*/
|
|
231
259
|
options(options) {
|
|
260
|
+
if (options.stdio && typeof options.stdio === 'string') {
|
|
261
|
+
// convert stdio to array
|
|
262
|
+
// This sets the default io values
|
|
263
|
+
// but can be overwritten when having a payload
|
|
264
|
+
const io = options.stdio;
|
|
265
|
+
options.stdio = Array(3).fill(io);
|
|
266
|
+
}
|
|
232
267
|
this.#options = mergeOptions(defaultOptions, options);
|
|
233
268
|
return this;
|
|
234
269
|
}
|
|
@@ -243,11 +278,12 @@ class SHDispatch {
|
|
|
243
278
|
|
|
244
279
|
/**
|
|
245
280
|
* Works for screen takeovers like editors
|
|
281
|
+
* @param {string} [payload]
|
|
246
282
|
* @returns {SpawnSyncResponse}
|
|
247
283
|
*/
|
|
248
|
-
runSync() {
|
|
284
|
+
runSync(payload) {
|
|
249
285
|
// @ts-ignore
|
|
250
|
-
return new SHExecute(this.#cmd, this.#options).runSync();
|
|
286
|
+
return new SHExecute(this.#cmd, this.#options).runSync(payload);
|
|
251
287
|
}
|
|
252
288
|
async kill() {
|
|
253
289
|
try {
|
|
@@ -287,7 +323,6 @@ class SHDispatch {
|
|
|
287
323
|
// Modified by: jorrit.duin+sh[AT]gmail.com
|
|
288
324
|
|
|
289
325
|
|
|
290
|
-
|
|
291
326
|
/**
|
|
292
327
|
* Creates a new SHDispatch object that represents a command to be executed.
|
|
293
328
|
*
|
|
@@ -450,6 +485,7 @@ const retry = async (count, a, b) => {
|
|
|
450
485
|
let delay = 0;
|
|
451
486
|
if (delayStatic > 0)
|
|
452
487
|
delay = delayStatic;
|
|
488
|
+
// @ts-ignore
|
|
453
489
|
if (delayGen) delay = delayGen.next().value;
|
|
454
490
|
lastErr = err;
|
|
455
491
|
if (count == 0)
|
package/package.json
CHANGED