@j-o-r/sh 0.0.2 → 0.0.3

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 CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  Execute shell commands from JavaScript.
4
4
 
5
- ![Alt text](https://raw.githubusercontent.com/j-o-r/sh/main/jor-sh.png "logo")
6
-
7
5
  ## Introduction
8
6
 
9
7
  BETA
@@ -52,7 +50,7 @@ const ar = within(async () => {
52
50
  ]);
53
51
  });
54
52
 
55
- ```
53
+ ```
56
54
 
57
55
  ```javascript
58
56
  const p = await retry(3, expBackoff(), () => SH`curl -s https://unreachable:`);
@@ -111,8 +109,8 @@ The `ProcessOutput` class collects output from the `ProcessPromise` shell proces
111
109
 
112
110
  ### Methods
113
111
 
114
- - **toString()**:
115
- - Returns a trimmed string combining `stderr` and `stdout`.
112
+ - **toString()**:
113
+ - Returns a trimmed string combining `stderr` and `stdout`.
116
114
  - This method is automatically invoked by various JavaScript methods for output consolidation.
117
115
  - **[inspect.custom]()**:
118
116
  - Custom inspection method used for debugging.
@@ -120,13 +118,9 @@ The `ProcessOutput` class collects output from the `ProcessPromise` shell proces
120
118
 
121
119
  ### Examples
122
120
 
123
- - Script for setting up my wokspace in [TMUX](./workspace.js)
121
+ - Script for setting up a wokspace in [TMUX](./workspace.js)
124
122
  - Take a look at the [test](./test/sh.js)
125
123
 
126
- ## Contributing
127
-
128
- Contributions are welcome. Please submit issues and pull requests on our [GitHub repository](https://github.com/j-o-r/sh).
129
-
130
124
  ## License
131
125
 
132
126
  This project is licensed under the Apache License, Version 2.0.
@@ -0,0 +1,44 @@
1
+ /// <reference types="node" />
2
+ export default ProcessOutput;
3
+ /**
4
+ * The ProcessPromise returns a ProcessOutput even when it fails, by rejecting it.
5
+ * The extension of the Error class is implemented to ensure compatibility when an Error is expected upon rejection.
6
+ */
7
+ declare class ProcessOutput extends Error {
8
+ /**
9
+ * @param {number} code - exit code
10
+ * @param {string} signal - SIGTERM ...
11
+ * @param {string} stdout - std reponse string
12
+ * @param {string} stderr - error reponse string
13
+ * @param {string} combined - stderr + stdout
14
+ * @param {string} message - Error message
15
+ */
16
+ constructor(code: number, signal: string, stdout?: string, stderr?: string, combined?: string, message?: string);
17
+ /**
18
+ * This string represents the standard output (stdout) from the child process.
19
+ * @returns {string}
20
+ */
21
+ get stdout(): string;
22
+ /**
23
+ * This string represents the error output (stderr) from the child process.
24
+ * @returns {string}
25
+ */
26
+ get stderr(): string;
27
+ /**
28
+ * This represents the exit code returned by the external process.
29
+ * @returns {number} The exit
30
+ */
31
+ get exitCode(): number;
32
+ /**
33
+ * This represents the exit signal, for example, "SIGTERM", received from the child process.
34
+ * @returns {string} The exit signal from the child
35
+ */
36
+ get signal(): string;
37
+ /**
38
+ * This method is used for debugging purposes. It displays the current state of the object
39
+ * when passed to the console.log function.
40
+ */
41
+ [inspect.custom](): string;
42
+ #private;
43
+ }
44
+ import { inspect } from 'node:util';
@@ -0,0 +1,147 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ export default ProcessPromise;
4
+ export type resolver = Function;
5
+ export type rejecter = Function;
6
+ export type PromiseConstruct = Function;
7
+ export type StdioOption = ('pipe' | 'ignore' | 'inherit' | number);
8
+ export type StdioOptions = Array<StdioOption> | StdioOption;
9
+ /**
10
+ * @typedef {Function} resolver
11
+ * @param {ProcessOutput} value
12
+ */
13
+ /**
14
+ * @typedef {Function} rejecter
15
+ * @param {ProcessOutput} value
16
+ */
17
+ /**
18
+ * @typedef {Function} PromiseConstruct
19
+ * @param {resolver} resolve
20
+ * @param {rejecter} reject
21
+ */
22
+ /**
23
+ * @typedef {('pipe' | 'ignore' | 'inherit' | number)} StdioOption
24
+ * @description Defines the stdio configuration for each of the standard streams.
25
+ *
26
+ * - 'pipe' creates a pipe between the child process and the parent process.
27
+ * The parent end of the pipe is exposed as a property on the `ChildProcess` object.
28
+ * - 'ignore' indicates that the child process's corresponding stdio file descriptor will be ignored.
29
+ * - 'inherit' passes the corresponding stdio stream to/from the child process.
30
+ * - Stream object to be used for the stdio stream.
31
+ * - Positive integer representing a file descriptor to be used for the stdio stream.
32
+ */
33
+ /**
34
+ * @typedef {Array<StdioOption>|StdioOption} StdioOptions
35
+ * @description
36
+ * Configures the stdio streams for the child process. This can be an array or a single StdioOption.
37
+ *
38
+ * Array Form: Specify the configuration for [stdin, stdout, stderr].
39
+ * - If array length is more than 3, additional positions correspond to extra streams.
40
+ * Single Value: This value will be applied to stdin, stdout, and stderr.
41
+ *
42
+ * Examples:
43
+ * - ['pipe', 'pipe', 'ignore']: Pipe stdin and stdout, ignore stderr.
44
+ * - 'inherit': Inherit all stdio streams from the parent.
45
+ */
46
+ /**
47
+ * class extends promise
48
+ */
49
+ declare class ProcessPromise extends Promise<any> {
50
+ /**
51
+ * @param {PromiseConstruct} p - A function that takes two arguments, resolve and reject.
52
+ */
53
+ constructor(p: PromiseConstruct);
54
+ /**
55
+ * Set the environment
56
+ * and the
57
+ * @param {string} cmd - Command to execute
58
+ * @param {string} from - Position in the codfe where this is triggred from
59
+ * @param {function} resolve - Promise resolve method
60
+ * @param {function} reject - Reject method
61
+ * @param {object} options - Settings (options default)
62
+ */
63
+ _bind(cmd: string, from: string, resolve: Function, reject: Function, options: object): void;
64
+ /**
65
+ * Run the promise
66
+ */
67
+ run(): this;
68
+ child: import("child_process").ChildProcessWithoutNullStreams & import("child_process").ChildProcessByStdio<import("stream").Writable, import("stream").Readable, import("stream").Readable> & import("child_process").ChildProcessByStdio<import("stream").Writable, import("stream").Readable, null> & import("child_process").ChildProcessByStdio<import("stream").Writable, null, import("stream").Readable> & import("child_process").ChildProcessByStdio<null, import("stream").Readable, import("stream").Readable> & import("child_process").ChildProcessByStdio<import("stream").Writable, null, null> & import("child_process").ChildProcessByStdio<null, import("stream").Readable, null> & import("child_process").ChildProcessByStdio<null, null, import("stream").Readable> & import("child_process").ChildProcessByStdio<null, null, null> & import("child_process").ChildProcess;
69
+ /**
70
+ * stdin child stream
71
+ * @retruns {Writeable}
72
+ */
73
+ get stdin(): null;
74
+ /**
75
+ * stdout child stream
76
+ * @retruns {Readable}
77
+ */
78
+ get stdout(): null;
79
+ /**
80
+ * stderr child stream
81
+ * @retruns {Readable}
82
+ */
83
+ get stderr(): null;
84
+ /**
85
+ * process exit code
86
+ * @returns {Promise<number>}
87
+ */
88
+ get exitCode(): Promise<number>;
89
+ catch(onrejected: any): Promise<any>;
90
+ /**
91
+ * Pipe the output to the input to the next Promise
92
+ * @example
93
+ * const res = await SH`ls -FLa`.pipe(SH`grep package.json`);
94
+ */
95
+ pipe(dest: any): ProcessPromise;
96
+ /**
97
+ * @private
98
+ * Set a postrun action, internal use only
99
+ * @param {function} f
100
+ */
101
+ private set _postrun(f);
102
+ /**
103
+ * Send a KILL signal to the child process
104
+ * @returns {Promise<number[]>} the pid numbers that has been killed
105
+ */
106
+ kill(signal?: string): Promise<number[]>;
107
+ stdio(stdin: any, stdout?: string, stderr?: string): this;
108
+ /**
109
+ * Do not throw
110
+ */
111
+ nothrow(): this;
112
+ /**
113
+ * supress log output
114
+ * SH.verbose = false; does the same
115
+ */
116
+ quiet(): this;
117
+ /**
118
+ * Show log output in the console
119
+ */
120
+ verbose(): this;
121
+ _quiet: boolean;
122
+ /**
123
+ * Set a timeout to kill a process
124
+ *
125
+ * @param {string} d - 10s, 1000ms
126
+ * @param {string} [signal] - default "SIGTERM" Signal to send to kill the proces
127
+ */
128
+ timeout(d: string, signal?: string): this;
129
+ _timeout: number;
130
+ _timeoutSignal: string;
131
+ /**
132
+ * stop execution for the next step
133
+ */
134
+ halt(): this;
135
+ /**
136
+ * @private
137
+ * Set a prerun action, internal use only
138
+ * @param {function} f
139
+ */
140
+ private set _prerun(f);
141
+ /**
142
+ * Is this promise halted?
143
+ * @returns {boolean}
144
+ */
145
+ get isHalted(): boolean;
146
+ #private;
147
+ }
@@ -44,7 +44,7 @@ import ProcessOutput from './ProcessOutput.js';
44
44
  * @param {rejecter} reject
45
45
  */
46
46
  /**
47
- * @typedef {('pipe' | 'ignore' | 'inherit' | Stream | number)} StdioOption
47
+ * @typedef {('pipe' | 'ignore' | 'inherit' | number)} StdioOption
48
48
  * @description Defines the stdio configuration for each of the standard streams.
49
49
  *
50
50
  * - 'pipe' creates a pipe between the child process and the parent process.
@@ -68,6 +68,10 @@ import ProcessOutput from './ProcessOutput.js';
68
68
  * - ['pipe', 'pipe', 'ignore']: Pipe stdin and stdout, ignore stderr.
69
69
  * - 'inherit': Inherit all stdio streams from the parent.
70
70
  */
71
+
72
+ /**
73
+ * class extends promise
74
+ */
71
75
  class ProcessPromise extends Promise {
72
76
  #command = '';
73
77
  #from = '';
@@ -89,6 +93,7 @@ class ProcessPromise extends Promise {
89
93
  * @param {PromiseConstruct} p - A function that takes two arguments, resolve and reject.
90
94
  */
91
95
  constructor(p) {
96
+ // @ts-ignore
92
97
  super(p)
93
98
  }
94
99
  /**
@@ -124,6 +129,7 @@ class ProcessPromise extends Promise {
124
129
  this.child = spawn(ENV.prefix, [this.#command], {
125
130
  cwd,
126
131
  shell,
132
+ // @ts-ignore
127
133
  stdio: this.#stdio,
128
134
  windowsHide: true,
129
135
  env: ENV.env,
@@ -148,7 +154,9 @@ class ProcessPromise extends Promise {
148
154
  });
149
155
  this.child.on('error', (err) => {
150
156
  const message = `${err.message}\n` +
157
+ // @ts-ignore
151
158
  ` errno: ${err.errno} (${errnoMessage(err.errno)})\n` +
159
+ // @ts-ignore
152
160
  ` code: ${err.code}\n` +
153
161
  ` at ${this.#from}`;
154
162
  this.#reject(new ProcessOutput(null, null, stdout, stderr, combined, message));
@@ -168,7 +176,9 @@ class ProcessPromise extends Promise {
168
176
  combined += data;
169
177
  };
170
178
  if (!this.#piped)
179
+ // @ts-ignore
171
180
  this.child.stdout?.on('data', onStdout); // If process is piped, don't collect or print output.
181
+ // @ts-ignore
172
182
  this.child.stderr?.on('data', onStderr); // Stderr should be printed regardless of piping.
173
183
  this.#postrun(); // In case $1.pipe($2), after both subprocesses are running, we can pipe $1.stdout to $2.stdin.
174
184
  if (this._timeout && this._timeoutSignal) {
@@ -216,9 +226,9 @@ class ProcessPromise extends Promise {
216
226
  * @returns {Promise<number>}
217
227
  */
218
228
  get exitCode() {
219
- return this.then((p) => p.exitCode, (p) => p.exitCode);
229
+ return this.#then((p) => p.exitCode, (p) => p.exitCode);
220
230
  }
221
- then(onfulfilled, onrejected) {
231
+ #then(onfulfilled, onrejected) {
222
232
  if (this.isHalted && !this.child) {
223
233
  throw new Error('The process is halted!');
224
234
  }
@@ -237,6 +247,7 @@ class ProcessPromise extends Promise {
237
247
  throw new Error('The pipe() method does not take strings. Forgot SH?');
238
248
  if (this.#resolved) {
239
249
  if (dest instanceof ProcessPromise)
250
+ // @ts-ignore
240
251
  dest.stdin.end(); // In case of piped stdin, we may want to close stdin of dest as well.
241
252
  throw new Error("The pipe() method shouldn't be called after promise is already resolved!");
242
253
  }
@@ -247,11 +258,13 @@ class ProcessPromise extends Promise {
247
258
  dest._postrun = () => {
248
259
  if (!dest.child)
249
260
  throw new Error('Access to stdin of pipe destination without creation a subprocess.');
261
+ // @ts-ignore
250
262
  this.stdout.pipe(dest.stdin);
251
263
  };
252
264
  return dest;
253
265
  }
254
266
  else {
267
+ // @ts-ignore
255
268
  this._postrun = () => this.stdout.pipe(dest);
256
269
  return this;
257
270
  }
package/lib/sh.d.ts ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Creates a new ProcessPromise object that represents a command to be executed.
3
+ */
4
+ export type Shell = Function;
5
+ /**
6
+ * Creates a new ProcessPromise object that represents a command to be executed.
7
+ *
8
+ * @typedef {Function} Shell
9
+ * @property {function(Array, ...*): ProcessPromise} execute - The function to execute the command.
10
+ * @property {boolean} verbose - A property to control verbosity.
11
+ *
12
+ * @param {Array} pieces - An array of string literals from a template literal.
13
+ * @param {...*} args - The values to be interpolated into the string literals.
14
+ * @returns {ProcessPromise} A ProcessPromise object that represents the command.
15
+ * @throws {Error} Throws an error if any of the string literals in `pieces` is undefined.
16
+ *
17
+ * @example
18
+ * const command = await SH`echo 'Hello, world!'`;
19
+ */
20
+ /** @type {Shell & { (pieces: TemplateStringsArray, ...args: any[]): ProcessPromise }} */
21
+ export const SH: Function & ((pieces: TemplateStringsArray, ...args: any[]) => ProcessPromise);
22
+ /**
23
+ * Change working directory
24
+ * @param {string} dir
25
+ */
26
+ export function cd(dir: string): void;
27
+ /**
28
+ * This function pauses or "sleeps" code execution for a specified duration.
29
+ * @param {string|number} duration - The duration to pause execution for, e.g., '100ms' or '3s'.
30
+ *
31
+ * @example
32
+ *
33
+ * const res = await Promise.all([
34
+ * SH`sleep 2; echo 2`, // Sleep for 2 seconds
35
+ * sleep(2), // Sleep for 2 seconds
36
+ * SH`sleep 3; echo 3` // Sleep for 3 seconds
37
+ * ]);
38
+ */
39
+ export function sleep(duration: string | number): Promise<any>;
40
+ /**
41
+ * This function retries a command a specified number of times.
42
+ * @example
43
+ * // Retry a command 3 times
44
+ * const p = await retry(3, () => SH`curl -s https://flipwrsi`);
45
+ *
46
+ * // Retry a command 3 times with an interval of 1 second between each try
47
+ * const p = await retry(3, '1s', () => SH`curl -s https://flipwrsi`);
48
+ *
49
+ * // Retry a command 3 times with irregular intervals using exponential backoff
50
+ * const p = await retry(3, expBackoff(), () => SH`curl -s https://flipwrsi`);
51
+ */
52
+ export function retry(count: any, a: any, b: any): Promise<any>;
53
+ /**
54
+ * This function reads the standard input (stdin) for the current process.
55
+ * It is used to get piped content into a script.
56
+ * @example
57
+ * const content = await stdin();
58
+ */
59
+ export function stdin(): Promise<string>;
60
+ /**
61
+ * Create a async context in an sync block
62
+ * @param {function} callback - async function
63
+ * @example
64
+ * const p = within(async () => {
65
+ * const res = await Promise.all([
66
+ * SH`sleep 1; echo 1`,
67
+ * SH`sleep 2; echo 2`,
68
+ * sleep(2),
69
+ * SH`sleep 3; echo 3`
70
+ * ]);
71
+ */
72
+ export function within(callback: Function): any;
73
+ export function expBackoff(max?: string, rand?: string): Generator<number, void, unknown>;
74
+ import ProcessPromise from './ProcessPromise.js';
@@ -29,7 +29,6 @@ import assert from 'node:assert';
29
29
  import { AsyncLocalStorage } from 'node:async_hooks';
30
30
  import { log, parseDuration, quote, quotePowerShell, } from './utils.js';
31
31
  import ProcessPromise from './ProcessPromise.js';
32
- // const processCwd = Symbol('processCwd');
33
32
  const storage = new AsyncLocalStorage();
34
33
 
35
34
  const defaults = {
@@ -43,6 +42,7 @@ defaults.prefix = 'set -euo pipefail;/usr/bin/env';
43
42
 
44
43
  /**
45
44
  * Escape CLI arguments
45
+ * @param {string[]} arg
46
46
  * @retruns {string}
47
47
  */
48
48
  const sanitizeArg = (arg) => {
@@ -56,23 +56,23 @@ const sanitizeArg = (arg) => {
56
56
  const getStore = () => {
57
57
  return storage.getStore() || defaults;
58
58
  }
59
-
60
59
  /**
61
- * Creates a new ProcessPromise object that represents a command to be executed.
62
- *
63
- * @typedef {Function} Shell
64
- * @type {function}
65
- * @param {Array} pieces - An array of string literals from a template literal.
66
- * @param {...*} args - The values to be interpolated into the string literals.
67
- * @returns {ProcessPromise} A ProcessPromise object that represents the command.
68
- * @throws {Error} Throws an error if any of the string literals in `pieces` is undefined.
69
- *
70
- * @property {boolean} verbose
71
- *
72
- * @example
73
- * const command = await SH`echo 'Hello, world!'`;
74
- */
75
- /** @type {Shell} */
60
+ * Creates a new ProcessPromise object that represents a command to be executed.
61
+ *
62
+ * @typedef {Function} Shell
63
+ * @property {function(Array, ...*): ProcessPromise} execute - The function to execute the command.
64
+ * @property {boolean} verbose - A property to control verbosity.
65
+ *
66
+ * @param {Array} pieces - An array of string literals from a template literal.
67
+ * @param {...*} args - The values to be interpolated into the string literals.
68
+ * @returns {ProcessPromise} A ProcessPromise object that represents the command.
69
+ * @throws {Error} Throws an error if any of the string literals in `pieces` is undefined.
70
+ *
71
+ * @example
72
+ * const command = await SH`echo 'Hello, world!'`;
73
+ */
74
+ /** @type {Shell & { (pieces: TemplateStringsArray, ...args: any[]): ProcessPromise }} */
75
+ // @ts-ignore
76
76
  const SH = new Proxy(function(pieces, ...args) {
77
77
  const from = new Error().stack.split(/^\s*at\s/m)[2].trim();
78
78
  if (pieces.some((p) => p == undefined)) {
@@ -172,7 +172,6 @@ const retry = async (count, a, b) => {
172
172
  else {
173
173
  delayStatic = parseDuration(a);
174
174
  }
175
- // console.log(assert(b));
176
175
  assert(b);
177
176
  callback = b;
178
177
  }
@@ -206,7 +205,7 @@ const retry = async (count, a, b) => {
206
205
  }
207
206
  /**
208
207
  * This function pauses or "sleeps" code execution for a specified duration.
209
- * @param {string} duration - The duration to pause execution for, e.g., '100ms' or '3s'.
208
+ * @param {string|number} duration - The duration to pause execution for, e.g., '100ms' or '3s'.
210
209
  *
211
210
  * @example
212
211
  *
@@ -245,7 +244,6 @@ function* expBackoff(max = '60s', rand = '100ms') {
245
244
  // process.chdir(SH['processCwd']);
246
245
  // }
247
246
  export {
248
- /** @type {Shell} */
249
247
  SH,
250
248
  cd,
251
249
  sleep,
package/lib/utils.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * Kills a process and all child processes of a given process ID in Linux/Posix.
4
+ * @param {number} processPid - The process ID.
5
+ * @param {string} signal - Signal to send.
6
+ * @retruns {Promise<number[]>} array with killed pid numbers
7
+ */
8
+ export function killProcesses(processPid: number, signal: string): Promise<any>;
9
+ export function noop(): void;
10
+ export function randomId(): string;
11
+ export function isString(obj: any): boolean;
12
+ export function quote(arg: any): any;
13
+ export function quotePowerShell(arg: any): any;
14
+ export function log(entry: any): void;
15
+ export function exitCodeInfo(exitCode: any): any;
16
+ export function errnoMessage(errno: any): any;
17
+ export function parseDuration(d: any): number;
18
+ export function formatCmd(cmd: any): string;
19
+ export { spawn };
20
+ import { spawn } from 'node:child_process';
package/package.json CHANGED
@@ -1,19 +1,23 @@
1
1
  {
2
2
  "name": "@j-o-r/sh",
3
- "author": "Jorrit Duin <jorrit.duin@gmail.com>",
3
+ "author": "Jorrit Duin <j-o-r@duin.work>",
4
4
  "type": "module",
5
- "version": "0.0.2",
5
+ "version": "0.0.3",
6
6
  "description": "Execute shell commands on Linux-based systems from javascript",
7
- "main": "src/sh.js",
7
+ "main": "lib/sh.js",
8
8
  "engines": {
9
- "node": ">=18.0.0"
9
+ "node": ">=20.0.0"
10
10
  },
11
11
  "scripts": {
12
- "test": "test/sh.js"
12
+ "test": "test/sh.js",
13
+ "publish": "npm run release && npm publish --access public",
14
+ "release": "npm pack --pack-destination=release",
15
+ "clear:types": "rm lib/*.d.ts",
16
+ "types": "tsc lib/*.js --declaration --allowJs --emitDeclarationOnly"
13
17
  },
14
18
  "repository": {
15
19
  "type": "git",
16
- "url": "https://github.com/j-o-r/sh.git"
20
+ "url": "https://codeberg.org/duin/sh"
17
21
  },
18
22
  "license": "Apache License, Version 2.0",
19
23
  "dependencies": {},
@@ -22,9 +26,9 @@
22
26
  "uvu": "^0.5.6"
23
27
  },
24
28
  "bugs": {
25
- "url": "https://github.com/j-o-r/sh/issues"
29
+ "url": "https://codeberg.org/duin/sh/issues"
26
30
  },
27
- "homepage": "https://github.com/j-o-r/sh",
31
+ "homepage": "https://codeberg.org/duin",
28
32
  "keywords": [
29
33
  "shell",
30
34
  "posix",
Binary file
File without changes
File without changes