@j-o-r/sh 1.0.0 → 1.0.1
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 +62 -27
- 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,7 @@
|
|
|
1
1
|
import { spawnSync, spawn, exec } from 'node:child_process';
|
|
2
2
|
|
|
3
|
+
// timeout when a process becomes inresponsive
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Kills a process and all child processes of a given process ID in Linux/Posix.
|
|
5
7
|
* @param {number} processPid - The process ID.
|
|
@@ -45,11 +47,11 @@ const killProcesses = (processPid, signal) => {
|
|
|
45
47
|
};
|
|
46
48
|
|
|
47
49
|
class SHExecute {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
#proc;
|
|
51
|
+
#command = '';
|
|
52
|
+
#options = {};
|
|
53
|
+
#stdout = '';
|
|
54
|
+
#stderr = '';
|
|
53
55
|
constructor(command, options = {}) {
|
|
54
56
|
this.#command = command;
|
|
55
57
|
this.#options = options;
|
|
@@ -57,26 +59,42 @@ class SHExecute {
|
|
|
57
59
|
this.#stdout = '';
|
|
58
60
|
this.#stderr = '';
|
|
59
61
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
/**
|
|
63
|
+
* @param {string} [payload] - data to write
|
|
64
|
+
* @retuns {Promise<object>}
|
|
65
|
+
*/
|
|
66
|
+
runSync(payload) {
|
|
67
|
+
if (payload && typeof payload !== 'string') {
|
|
68
|
+
throw new Error('Argument is not a string');
|
|
69
|
+
}
|
|
70
|
+
let { cwd, shell, env, stdio } = this.#options;
|
|
71
|
+
// pipe need to be set on stdin when posting a payload
|
|
72
|
+
if (payload) stdio[0] = 'pipe';
|
|
73
|
+
const input = payload || undefined;
|
|
62
74
|
return spawnSync(this.#options.prefix, [this.#command], {
|
|
63
75
|
cwd,
|
|
64
76
|
shell,
|
|
65
77
|
stdio,
|
|
66
78
|
windowsHide: true,
|
|
67
79
|
env,
|
|
80
|
+
input
|
|
68
81
|
});
|
|
69
82
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
/**
|
|
84
|
+
* @param {string} [payload] - data to write
|
|
85
|
+
* @retuns {Promise<string>}
|
|
86
|
+
*/
|
|
74
87
|
run(payload) {
|
|
88
|
+
let to = 0;
|
|
75
89
|
if (payload && typeof payload !== 'string') {
|
|
76
90
|
throw new Error('Argument is not a string');
|
|
77
|
-
}
|
|
91
|
+
}
|
|
92
|
+
if (this.#options.timeout) {
|
|
93
|
+
to = this.#options.timeout;
|
|
94
|
+
}
|
|
78
95
|
let { cwd, shell, env, stdio } = this.#options;
|
|
79
|
-
|
|
96
|
+
// pipe need to be set on stdin when posting a payload
|
|
97
|
+
if (payload) stdio[0] = 'pipe';
|
|
80
98
|
this.#proc = spawn(this.#options.prefix, [this.#command], {
|
|
81
99
|
cwd,
|
|
82
100
|
shell,
|
|
@@ -92,12 +110,19 @@ class SHExecute {
|
|
|
92
110
|
this.#proc.stderr?.on('data', (data) => {
|
|
93
111
|
this.#stderr += data;
|
|
94
112
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
113
|
+
if (payload) {
|
|
114
|
+
this.#proc.stdin.end(payload);
|
|
115
|
+
}
|
|
99
116
|
return new Promise((resolve, reject) => {
|
|
117
|
+
let timeout;
|
|
118
|
+
if (to > 0) {
|
|
119
|
+
timeout = setTimeout(async () => {
|
|
120
|
+
await this.#proc.kill();
|
|
121
|
+
reject(new Error('Process timed out'));
|
|
122
|
+
}, to); // options.timeout
|
|
123
|
+
}
|
|
100
124
|
this.#proc.on('close', (code) => {
|
|
125
|
+
if (timeout) clearTimeout(timeout);
|
|
101
126
|
if (code === 0) {
|
|
102
127
|
resolve(this.#stdout.trim());
|
|
103
128
|
} else {
|
|
@@ -110,9 +135,9 @@ class SHExecute {
|
|
|
110
135
|
});
|
|
111
136
|
});
|
|
112
137
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
138
|
+
/**
|
|
139
|
+
* @returns {Promise<number[]>}
|
|
140
|
+
*/
|
|
116
141
|
async kill(signal = 'SIGTERM') {
|
|
117
142
|
if (!this.#proc) throw new Error('Trying to kill a process without creating one.');
|
|
118
143
|
if (!this.#proc.pid) throw new Error('The process pid is undefined.');
|
|
@@ -138,7 +163,8 @@ class SHExecute {
|
|
|
138
163
|
* @property {NodeJS.ProcessEnv} [env] - The environment variables.
|
|
139
164
|
* @property {string} [shell] - The shell to use for execution.
|
|
140
165
|
* @property {string} [prefix] - The prefix commands to ensure a safe execution environment.
|
|
141
|
-
* @property {StdioOption
|
|
166
|
+
* @property {StdioOptions|StdioOption} [stdio] - The stdio configuration.
|
|
167
|
+
* @property {number} [timeout] - default 20000: a timeout error is triggerd when a process execution time is exceeded, 0 is no timeout
|
|
142
168
|
*/
|
|
143
169
|
/**
|
|
144
170
|
* @typedef {('pipe' | 'ignore' | 'inherit' | number)} StdioOption
|
|
@@ -180,9 +206,9 @@ const hasProp = (o, p) => {
|
|
|
180
206
|
|
|
181
207
|
/**
|
|
182
208
|
* Merge property values while maintaining the fixed set of props in the original object
|
|
183
|
-
* @param {
|
|
184
|
-
* @param {
|
|
185
|
-
* @
|
|
209
|
+
* @param {SHOptions} predefined - original object
|
|
210
|
+
* @param {SHOptions} options - object with new values
|
|
211
|
+
* @returns {SHOptions}
|
|
186
212
|
*/
|
|
187
213
|
const mergeOptions = (predefined, options) => {
|
|
188
214
|
// Extract the keys from the predefined object
|
|
@@ -208,7 +234,8 @@ const defaultOptions = {
|
|
|
208
234
|
env: process.env,
|
|
209
235
|
shell: 'bash',
|
|
210
236
|
prefix: 'set -euo pipefail;/usr/bin/env',
|
|
211
|
-
stdio: ['inherit', 'pipe', 'pipe']
|
|
237
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
238
|
+
timeout: 10000 // when 0 there is no timeout
|
|
212
239
|
};
|
|
213
240
|
|
|
214
241
|
|
|
@@ -229,6 +256,13 @@ class SHDispatch {
|
|
|
229
256
|
* @returns {SHDispatch}
|
|
230
257
|
*/
|
|
231
258
|
options(options) {
|
|
259
|
+
if (options.stdio && typeof options.stdio === 'string') {
|
|
260
|
+
// convert stdio to array
|
|
261
|
+
// This sets the default io values
|
|
262
|
+
// but can be overwritten when having a payload
|
|
263
|
+
const io = options.stdio;
|
|
264
|
+
options.stdio = Array(3).fill(io);
|
|
265
|
+
}
|
|
232
266
|
this.#options = mergeOptions(defaultOptions, options);
|
|
233
267
|
return this;
|
|
234
268
|
}
|
|
@@ -243,11 +277,12 @@ class SHDispatch {
|
|
|
243
277
|
|
|
244
278
|
/**
|
|
245
279
|
* Works for screen takeovers like editors
|
|
280
|
+
* @param {string} [payload]
|
|
246
281
|
* @returns {SpawnSyncResponse}
|
|
247
282
|
*/
|
|
248
|
-
runSync() {
|
|
283
|
+
runSync(payload) {
|
|
249
284
|
// @ts-ignore
|
|
250
|
-
return new SHExecute(this.#cmd, this.#options).runSync();
|
|
285
|
+
return new SHExecute(this.#cmd, this.#options).runSync(payload);
|
|
251
286
|
}
|
|
252
287
|
async kill() {
|
|
253
288
|
try {
|
package/package.json
CHANGED