@zokugun/exec-lite 0.0.0 → 0.2.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.
Files changed (63) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +258 -34
  3. package/lib/cjs/combineSignals.cjs +19 -0
  4. package/lib/cjs/combineSignals.d.cts +1 -0
  5. package/lib/cjs/exec-process.cjs +221 -0
  6. package/lib/cjs/exec-process.d.cts +37 -0
  7. package/lib/cjs/exec.cjs +9 -0
  8. package/lib/cjs/exec.d.cts +2 -0
  9. package/lib/cjs/index.cjs +7 -0
  10. package/lib/cjs/index.d.cts +3 -0
  11. package/lib/cjs/types.cjs +2 -0
  12. package/lib/cjs/types.d.cts +56 -0
  13. package/lib/cjs/utils/command/detectShebang.cjs +36 -0
  14. package/lib/cjs/utils/command/detectShebang.d.cts +1 -0
  15. package/lib/cjs/utils/command/normalizeCommand.cjs +59 -0
  16. package/lib/cjs/utils/command/normalizeCommand.d.cts +6 -0
  17. package/lib/cjs/utils/command/resolveCommand.cjs +43 -0
  18. package/lib/cjs/utils/command/resolveCommand.d.cts +2 -0
  19. package/lib/cjs/utils/env/addNodeBinToPath.cjs +24 -0
  20. package/lib/cjs/utils/env/addNodeBinToPath.d.cts +2 -0
  21. package/lib/cjs/utils/env/computeEnv.cjs +17 -0
  22. package/lib/cjs/utils/env/computeEnv.d.cts +2 -0
  23. package/lib/cjs/utils/env/getPathFromEnv.cjs +22 -0
  24. package/lib/cjs/utils/env/getPathFromEnv.d.cts +2 -0
  25. package/lib/cjs/utils/env/types.cjs +2 -0
  26. package/lib/cjs/utils/env/types.d.cts +5 -0
  27. package/lib/cjs/utils/normalizeOptions.cjs +31 -0
  28. package/lib/cjs/utils/normalizeOptions.d.cts +2 -0
  29. package/lib/cjs/utils/stream/combineStreams.cjs +25 -0
  30. package/lib/cjs/utils/stream/combineStreams.d.cts +2 -0
  31. package/lib/cjs/utils/stream/readStream.cjs +16 -0
  32. package/lib/cjs/utils/stream/readStream.d.cts +5 -0
  33. package/lib/esm/combineSignals.d.mts +1 -0
  34. package/lib/esm/combineSignals.mjs +16 -0
  35. package/lib/esm/exec-process.d.mts +37 -0
  36. package/lib/esm/exec-process.mjs +214 -0
  37. package/lib/esm/exec.d.mts +2 -0
  38. package/lib/esm/exec.mjs +6 -0
  39. package/lib/esm/index.d.mts +3 -0
  40. package/lib/esm/index.mjs +2 -0
  41. package/lib/esm/types.d.mts +56 -0
  42. package/lib/esm/types.mjs +1 -0
  43. package/lib/esm/utils/command/detectShebang.d.mts +1 -0
  44. package/lib/esm/utils/command/detectShebang.mjs +30 -0
  45. package/lib/esm/utils/command/normalizeCommand.d.mts +6 -0
  46. package/lib/esm/utils/command/normalizeCommand.mjs +53 -0
  47. package/lib/esm/utils/command/resolveCommand.d.mts +2 -0
  48. package/lib/esm/utils/command/resolveCommand.mjs +37 -0
  49. package/lib/esm/utils/env/addNodeBinToPath.d.mts +2 -0
  50. package/lib/esm/utils/env/addNodeBinToPath.mjs +18 -0
  51. package/lib/esm/utils/env/computeEnv.d.mts +2 -0
  52. package/lib/esm/utils/env/computeEnv.mjs +14 -0
  53. package/lib/esm/utils/env/getPathFromEnv.d.mts +2 -0
  54. package/lib/esm/utils/env/getPathFromEnv.mjs +19 -0
  55. package/lib/esm/utils/env/types.d.mts +5 -0
  56. package/lib/esm/utils/env/types.mjs +1 -0
  57. package/lib/esm/utils/normalizeOptions.d.mts +2 -0
  58. package/lib/esm/utils/normalizeOptions.mjs +25 -0
  59. package/lib/esm/utils/stream/combineStreams.d.mts +2 -0
  60. package/lib/esm/utils/stream/combineStreams.mjs +22 -0
  61. package/lib/esm/utils/stream/readStream.d.mts +5 -0
  62. package/lib/esm/utils/stream/readStream.mjs +13 -0
  63. package/package.json +122 -3
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2026-present Baptiste Augrain
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,52 +1,276 @@
1
- ⚠️ Important Notice ⚠️
2
- ----------------------
1
+ [@zokugun/exec-lite](https://github.com/zokugun/node-exec-lite)
2
+ ===============================================================
3
3
 
4
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
5
+ [![NPM Version](https://img.shields.io/npm/v/@zokugun/exec-lite?color=brightgreen)](https://www.npmjs.com/package/@zokugun/exec-lite)
6
+ [![NodeJS Version](https://img.shields.io/badge/node.js-%3E%3D%2018.20-green)](#requirements)
7
+ [![Modules](https://img.shields.io/badge/modules-ESM%20%7C%20CJS-green)](#requirements)
8
+ [![Ko-fi](https://img.shields.io/badge/Ko--fi-f87171?logo=kofi&logoColor=white)](https://ko-fi.com/daiyam)
9
+ [![Liberapay](https://img.shields.io/badge/Liberapay-facc15?logo=liberapay&logoColor=black)](https://liberapay.com/daiyam/donate)
10
+ [![PayPal](https://img.shields.io/badge/PayPal-5277C3?logo=paypal&logoColor=white)](https://paypal.me/daiyam99)
5
11
 
6
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
12
+ > Run external commands and manage their output easily. You get simple success or failure objects containing exit codes, execution time, and all captured text from standard output and error streams.
7
13
 
8
- Purpose
9
- -------
14
+ Features
15
+ --------
16
+
17
+ - **Process Management**: Spawn and manage external commands with a clean API.
18
+ - **Result Types**: Get structured success and failure objects for every run.
19
+ - **Unified Output**: Combines stdout and stderr into a single stream for easier logging.
20
+ - **Windows Normalization**: Automatically handles `cmd.exe` and path escaping for Windows.
21
+ - **Automated Resolution**: Locate binaries automatically using PATH and file extensions.
22
+ - **Process Piping**: Chain multiple commands together to pass data between them.
23
+ - **Abort Support**: Cancel running processes using standard abort signals.
24
+ - **Exit Tracking**: Capture precise exit codes and signal codes from tasks.
25
+ - **Execution Timing**: Measure the exact duration of a command in milliseconds.
26
+
27
+ Installation
28
+ ------------
29
+
30
+ ```bash
31
+ npm add @zokugun/exec-lite
32
+ ```
33
+
34
+ Requirements
35
+ ------------
36
+
37
+ - **Node.js**: `v18.20` or higher
38
+ - **Modules**: ECMAScript Modules (`ESM`) or CommonJS (`CJS`)
39
+
40
+ Quick Start
41
+ -----------
42
+
43
+ ### 1. Basic Execution
44
+
45
+ Run a command and wait for the final output. Use this when you only need the end result.
46
+
47
+ ```typescript
48
+ import { exec } from '@zokugun/exec-lite';
49
+
50
+ async function runSimpleCommand(): Promise<void> {
51
+ const result = await exec('echo', ['hello world']);
52
+
53
+ if(result.fails) {
54
+ console.error(`Failed with exit code: ${result.value.exitCode}`);
55
+
56
+ return;
57
+ }
58
+
59
+ const { stdout } = result.value;
60
+
61
+ console.log(`Output: ${stdout}`);
62
+ }
63
+ ```
64
+
65
+ ### 2. Streaming Output
66
+
67
+ Iterate over the process output in real-time.
68
+
69
+ ```typescript
70
+ import { exec } from '@zokugun/exec-lite';
71
+
72
+ async function runPipedCommands(): Promise<void> {
73
+ const task = exec('npm', ['install']);
74
+
75
+ for await(const line of task) {
76
+ console.log(`Log: ${line}`);
77
+ }
78
+
79
+ if(task.failed) {
80
+ console.error(`Failed with exit code: ${task.exitCode}`);
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### 3. Piping Processes
86
+
87
+ Chain multiple commands together using the `.pipe()` method. The output of the first process is passed as the input to the second.
88
+
89
+ ```typescript
90
+ import { exec } from '@zokugun/exec-lite';
91
+
92
+ async function runPipedCommands(): Promise<void> {
93
+ const result = await exec('cat', ['package.json']).pipe('grep', ['dependencies']);
94
+
95
+ if(result.fails) {
96
+ console.log('Pipeline failed');
97
+
98
+ return;
99
+ }
100
+
101
+ console.log(`Filtered output: ${result.value.stdout}`);
102
+ }
103
+
104
+ await runPipedCommands();
105
+ ```
106
+
107
+ API Reference
108
+ -------------
109
+
110
+ ### Functions
10
111
 
11
- This package exists to:
112
+ #### `exec`
12
113
 
13
- 1. Configure OIDC trusted publishing for the package name `@zokugun/exec-lite`
14
- 2. Enable secure, token-less publishing from CI/CD workflows
15
- 3. Establish provenance for packages published under this name
114
+ ```typescript
115
+ export function exec(
116
+ command: string,
117
+ args?: readonly string[] | null,
118
+ options?: Partial<Options>,
119
+ ): Subprocess;
120
+ ```
16
121
 
17
- What Is OIDC Trusted Publishing?
18
- --------------------------------
122
+ Spawns a child process and returns a `Subprocess` instance. The process starts immediately on return.
19
123
 
20
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
124
+ **Caveats:**
21
125
 
22
- Setup Instructions
23
- ------------------
126
+ - `exec` does not throw. Errors surface through the `fails` flag of the `Result`.
127
+ - On Windows, batch files (`.bat`, `.cmd`) are wrapped in `cmd.exe` with verbatim argument handling.
24
128
 
25
- To properly configure OIDC trusted publishing for this package:
129
+ ---
26
130
 
27
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
28
- 2. Configure the trusted publisher (e.g., GitHub Actions)
29
- 3. Specify the repository and workflow that should be allowed to publish
30
- 4. Use the configured workflow to publish your actual package
131
+ ### Classes
31
132
 
32
- Do not Use This Package
33
- -----------------------
133
+ #### `ExecProcess`
34
134
 
35
- This package is a placeholder for OIDC configuration only. It:
135
+ ```typescript
136
+ class ExecProcess implements Subprocess { ... }
137
+ ```
36
138
 
37
- - Contains no executable code
38
- - Provides no functionality
39
- - Should not be installed as a dependency
40
- - Exists only for administrative purposes
139
+ The class behind every subprocess returned by `exec`. You rarely need to construct it directly — use `exec()` instead.
41
140
 
42
- More Information
43
- ----------------
141
+ **Key properties:**
44
142
 
45
- For more details about npm's trusted publishing feature, see:
143
+ | Property | Type | Description |
144
+ | ------------- | --------------------------- | ------------------------------------------------------ |
145
+ | `.aborted` | `boolean` | True if the process was aborted via signal |
146
+ | `.error` | `Error \| undefined` | The error that caused failure, if any |
147
+ | `.exitCode` | `number \| undefined` | Exit code after the process closes |
148
+ | `.failed` | `boolean` | True if exit code is non-zero or a signal was received |
149
+ | `.killed` | `boolean` | True if `kill()` was called |
150
+ | `.pid` | `number \| undefined` | OS process ID |
151
+ | `.process` | `ChildProcess \| undefined` | The raw Node.js child process handle |
152
+ | `.signalCode` | `string \| undefined` | Signal code that terminated the process |
46
153
 
47
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
48
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
154
+ **Key methods:**
155
+
156
+ | Method | Returns | Description |
157
+ | --------------------------------- | ------------ | ----------------------------------------------------------------- |
158
+ | `.kill(signal?)` | `boolean` | Sends a signal to the process. Returns false if no handle exists. |
159
+ | `.pipe(command, args?, options?)` | `Subprocess` | Chains another command, piping this stdout into its stdin. |
160
+ | `.spawn()` | `void` | Starts the process. Called automatically by `exec()`. |
161
+
162
+ ---
163
+
164
+ ### Types
165
+
166
+ #### `ExecResult`
167
+
168
+ A discriminated union wrapping success or failure:
169
+
170
+ ```typescript
171
+ type ExecResult = Result<ExecSuccess, ExecFailure>;
172
+ ```
173
+
174
+ ---
175
+
176
+ #### `ExecSuccess`
177
+
178
+ Returned when the process exits with code 0 and no error occurred.
179
+
180
+ ```typescript
181
+ type ExecSuccess = {
182
+ all: string; // combined stdout + stderr
183
+ command: string;
184
+ durationMs: number;
185
+ exitCode: number;
186
+ stdout: string;
187
+ stderr: string;
188
+ };
189
+ ```
190
+
191
+ ---
192
+
193
+ #### `ExecFailure`
194
+
195
+ Returned when the process errors, exits non-zero, or gets killed.
196
+
197
+ ```typescript
198
+ type ExecFailure = {
199
+ all?: string;
200
+ canceled: boolean; // true if aborted via signal
201
+ command: string;
202
+ durationMs: number;
203
+ error?: Error;
204
+ exitCode?: number;
205
+ signalCode?: string;
206
+ stderr?: string;
207
+ stdout?: string;
208
+ };
209
+ ```
210
+
211
+ ---
212
+
213
+ #### `Subprocess`
214
+
215
+ The shape returned by `exec()`. It is:
216
+
217
+ - A **promise-like** — resolves to `ExecResult` when awaited.
218
+ - An **async iterable** — yields lines from stdout + stderr.
219
+ - A **live handle** — inspect `.exitCode`, `.pid`, etc. at any time after spawn.
220
+
221
+ ```typescript
222
+ type Subprocess = AsyncIterable<string> & PromiseLike<ExecResult> & { ... };
223
+ ```
224
+
225
+ ---
226
+
227
+ #### `Options`
228
+
229
+ Extended `SpawnOptions` with extra fields:
230
+
231
+ | Field | Type | Default | Description |
232
+ | ------------------- | ------------------------------------- | -------------------- | ------------------------------------- |
233
+ | `cwd` | `string` | `process.cwd()` | Working directory |
234
+ | `env` | `Record<string, string>` | inherited + computed | Environment variables |
235
+ | `localDir` | `string` | `cwd` | Directory to resolve local paths from |
236
+ | `preferLocal` | `boolean` | `false` | Prepends `node_modules/.bin` to PATH |
237
+ | `signal` | `AbortSignal` | none | Abort signal for cancellation |
238
+ | `stdin` | `string \| Subprocess \| { process }` | `pipe` | Input source |
239
+ | `stdio` | | none | Standard I/O configurations. |
240
+ | `stdout` / `stderr` | `StdioOption` | none | Stream configuration |
241
+ | `timeout` | `number` | none | Auto-abort after N milliseconds |
242
+
243
+ Contributions
244
+ -------------
245
+
246
+ Contributions are most welcome. Please:
247
+
248
+ - Open issues and feature requests under the repository discussions.
249
+ - Follow the [`CONTRIBUTING.md`](./CONTRIBUTING.md).
250
+
251
+ Donations
252
+ ---------
253
+
254
+ Support this project by becoming a financial contributor.
255
+
256
+ <table>
257
+ <tr>
258
+ <td><img src="https://raw.githubusercontent.com/daiyam/assets/master/icons/256/funding_kofi.png" alt="Ko-fi" width="80px" height="80px"></td>
259
+ <td><a href="https://ko-fi.com/daiyam" target="_blank">ko-fi.com/daiyam</a></td>
260
+ </tr>
261
+ <tr>
262
+ <td><img src="https://raw.githubusercontent.com/daiyam/assets/master/icons/256/funding_liberapay.png" alt="Liberapay" width="80px" height="80px"></td>
263
+ <td><a href="https://liberapay.com/daiyam/donate" target="_blank">liberapay.com/daiyam/donate</a></td>
264
+ </tr>
265
+ <tr>
266
+ <td><img src="https://raw.githubusercontent.com/daiyam/assets/master/icons/256/funding_paypal.png" alt="PayPal" width="80px" height="80px"></td>
267
+ <td><a href="https://paypal.me/daiyam99" target="_blank">paypal.me/daiyam99</a></td>
268
+ </tr>
269
+ </table>
270
+
271
+ License
272
+ -------
49
273
 
50
- --------------------
274
+ Copyright &copy; 2026-present Baptiste Augrain
51
275
 
52
- **Maintained for OIDC setup purposes only**
276
+ Licensed under the [MIT license](https://opensource.org/licenses/MIT).
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.combineSignals = combineSignals;
4
+ function combineSignals(signals) {
5
+ const controller = new AbortController();
6
+ for (const signal of signals) {
7
+ if (signal.aborted) {
8
+ controller.abort();
9
+ return signal;
10
+ }
11
+ const onAbort = () => {
12
+ controller.abort(signal.reason);
13
+ };
14
+ signal.addEventListener('abort', onAbort, {
15
+ signal: controller.signal,
16
+ });
17
+ }
18
+ return controller.signal;
19
+ }
@@ -0,0 +1 @@
1
+ export declare function combineSignals(signals: AbortSignal[]): AbortSignal;
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ExecProcess = void 0;
7
+ const node_child_process_1 = require("node:child_process");
8
+ const node_readline_1 = __importDefault(require("node:readline"));
9
+ const xtry_1 = require("@zokugun/xtry");
10
+ const exec_js_1 = require("./exec.cjs");
11
+ const normalizeCommand_js_1 = require("./utils/command/normalizeCommand.cjs");
12
+ const normalizeOptions_js_1 = require("./utils/normalizeOptions.cjs");
13
+ const combineStreams_js_1 = require("./utils/stream/combineStreams.cjs");
14
+ const readStream_js_1 = require("./utils/stream/readStream.cjs");
15
+ class ExecProcess {
16
+ _aborted = false;
17
+ _args;
18
+ _command;
19
+ _failed = false;
20
+ _options;
21
+ _process;
22
+ _processClosed;
23
+ _resolveClose;
24
+ _signal;
25
+ _start;
26
+ _streamErr;
27
+ _streamOut;
28
+ _thrownError;
29
+ constructor(command, args, options) {
30
+ this._options = {
31
+ timeout: undefined,
32
+ windowsHide: true,
33
+ ...options,
34
+ };
35
+ this._command = command;
36
+ this._args = args ?? [];
37
+ this._processClosed = new Promise((resolve) => {
38
+ this._resolveClose = resolve;
39
+ });
40
+ } // }}}
41
+ get aborted() {
42
+ return this._aborted;
43
+ } // }}}
44
+ get error() {
45
+ return this._thrownError;
46
+ } // }}}
47
+ get exitCode() {
48
+ return this._process?.exitCode ?? undefined;
49
+ } // }}}
50
+ get failed() {
51
+ return this._failed;
52
+ } // }}}
53
+ get killed() {
54
+ return this._process?.killed === true;
55
+ } // }}}
56
+ get pid() {
57
+ return this._process?.pid;
58
+ } // }}}
59
+ get process() {
60
+ return this._process;
61
+ } // }}}
62
+ get signalCode() {
63
+ return this._process?.signalCode ?? undefined;
64
+ } // }}}
65
+ kill(signal) {
66
+ return this._process?.kill(signal) === true;
67
+ } // }}}
68
+ pipe(command, args, options) {
69
+ return (0, exec_js_1.exec)(command, args, {
70
+ ...options,
71
+ stdin: this,
72
+ });
73
+ } // }}}
74
+ spawn() {
75
+ this._start = process.hrtime.bigint();
76
+ this.reset();
77
+ const normalizedOptions = (0, normalizeOptions_js_1.normalizeOptions)(this._options);
78
+ if (normalizedOptions.signal) {
79
+ this._signal = normalizedOptions.signal;
80
+ }
81
+ const { command: spawnCommand, args: spawnArgs, options: spawnOptions } = (0, normalizeCommand_js_1.normalizeCommand)(this._command, this._args, normalizedOptions);
82
+ let task;
83
+ try {
84
+ task = (0, node_child_process_1.spawn)(spawnCommand, spawnArgs, spawnOptions);
85
+ }
86
+ catch (error) {
87
+ if (error instanceof Error) {
88
+ this._thrownError = error;
89
+ }
90
+ else {
91
+ this._thrownError = new Error(String(error));
92
+ }
93
+ return;
94
+ }
95
+ if (task.stderr) {
96
+ this._streamErr = task.stderr;
97
+ }
98
+ if (task.stdout) {
99
+ this._streamOut = task.stdout;
100
+ }
101
+ this._process = task;
102
+ task.once('error', this.onError.bind(this));
103
+ task.once('close', this.onClose.bind(this));
104
+ if (task.stdin) {
105
+ const { stdin } = this._options;
106
+ if (typeof stdin === 'string') {
107
+ task.stdin.end(stdin);
108
+ }
109
+ else if (typeof stdin === 'object' && stdin !== null && 'process' in stdin) {
110
+ stdin.process?.stdout?.pipe(task.stdin);
111
+ }
112
+ }
113
+ } // }}}
114
+ // eslint-disable-next-line ts/explicit-member-accessibility
115
+ async *[Symbol.asyncIterator]() {
116
+ const handle = this._process;
117
+ if (!handle) {
118
+ return;
119
+ }
120
+ const streams = [];
121
+ if (this._streamErr) {
122
+ streams.push(this._streamErr);
123
+ }
124
+ if (this._streamOut) {
125
+ streams.push(this._streamOut);
126
+ }
127
+ const streamCombined = (0, combineStreams_js_1.combineStreams)(streams);
128
+ const rl = node_readline_1.default.createInterface({ input: streamCombined });
129
+ for await (const chunk of rl) {
130
+ // eslint-disable-next-line ts/no-unnecessary-type-conversion
131
+ yield chunk.toString();
132
+ }
133
+ await this._processClosed;
134
+ handle.removeAllListeners();
135
+ if (this._thrownError || (this.exitCode !== 0 && this.exitCode !== undefined) || this.signalCode !== undefined) {
136
+ this._failed = true;
137
+ }
138
+ } // }}}
139
+ // eslint-disable-next-line ts/promise-function-async
140
+ then(onfulfilled, onrejected) {
141
+ return this.toResult().then(onfulfilled, onrejected);
142
+ } // }}}
143
+ onClose() {
144
+ if (this._resolveClose) {
145
+ this._resolveClose();
146
+ }
147
+ } // }}}
148
+ onError(error) {
149
+ if (error.name === 'AbortError' && (!(error.cause instanceof Error) || error.cause.name !== 'TimeoutError')) {
150
+ this._aborted = true;
151
+ return;
152
+ }
153
+ this._thrownError = error;
154
+ } // }}}
155
+ reset() {
156
+ this._aborted = false;
157
+ this._process = undefined;
158
+ this._processClosed = new Promise((resolve) => {
159
+ this._resolveClose = resolve;
160
+ });
161
+ this._signal = undefined;
162
+ this._streamErr = undefined;
163
+ this._streamOut = undefined;
164
+ this._thrownError = undefined;
165
+ } // }}}
166
+ toFailure(error) {
167
+ this._failed = true;
168
+ const result = {
169
+ all: '',
170
+ canceled: this._signal?.aborted ?? false,
171
+ command: this._command,
172
+ durationMs: Number(process.hrtime.bigint() - this._start) / 1e6,
173
+ error,
174
+ exitCode: this.exitCode,
175
+ signalCode: this.signalCode,
176
+ };
177
+ return result;
178
+ } // }}}
179
+ async toResult() {
180
+ if (this._thrownError) {
181
+ return (0, xtry_1.err)(this.toFailure(this._thrownError));
182
+ }
183
+ const task = this._process;
184
+ if (!task) {
185
+ return (0, xtry_1.err)(this.toFailure(new Error('No process was started')));
186
+ }
187
+ const context = { all: '' };
188
+ const [stdout, stderr] = await Promise.all([
189
+ this._streamOut ? (0, readStream_js_1.readStream)(this._streamOut, context) : '',
190
+ this._streamErr ? (0, readStream_js_1.readStream)(this._streamErr, context) : '',
191
+ ]);
192
+ await this._processClosed;
193
+ const { stdin } = this._options;
194
+ if (stdin && typeof stdin !== 'string') {
195
+ await stdin;
196
+ }
197
+ task.removeAllListeners();
198
+ if (this._thrownError) {
199
+ return (0, xtry_1.err)(this.toFailure(this._thrownError));
200
+ }
201
+ const result = {
202
+ all: context.all,
203
+ command: this._command,
204
+ durationMs: Number(process.hrtime.bigint() - this._start) / 1e6,
205
+ exitCode: this.exitCode,
206
+ stderr,
207
+ stdout,
208
+ };
209
+ // if(this._options.stdin instanceof ExecProcess) {
210
+ // result.pipedFrom = this._options.stdin;
211
+ // }
212
+ if ((this.exitCode !== 0 && this.exitCode !== undefined) || this.signalCode !== undefined) {
213
+ this._failed = true;
214
+ const failure = result;
215
+ failure.signalCode = this.signalCode;
216
+ return (0, xtry_1.err)(failure);
217
+ }
218
+ return (0, xtry_1.ok)(result);
219
+ } // }}}
220
+ }
221
+ exports.ExecProcess = ExecProcess;
@@ -0,0 +1,37 @@
1
+ import type { ChildProcess } from 'node:child_process';
2
+ import type { Readable } from 'node:stream';
3
+ import type { ExecFailure, ExecResult, KillSignal, Options, Subprocess } from './types.cjs';
4
+ export declare class ExecProcess implements Subprocess {
5
+ protected _aborted: boolean;
6
+ protected _args: readonly string[];
7
+ protected _command: string;
8
+ protected _failed: boolean;
9
+ protected _options: Partial<Options>;
10
+ protected _process?: ChildProcess;
11
+ protected _processClosed: Promise<void>;
12
+ protected _resolveClose?: () => void;
13
+ protected _signal?: AbortSignal;
14
+ protected _start?: bigint;
15
+ protected _streamErr?: Readable;
16
+ protected _streamOut?: Readable;
17
+ protected _thrownError?: Error;
18
+ constructor(command: string, args?: readonly string[] | null, options?: Partial<Options>);
19
+ get aborted(): boolean;
20
+ get error(): Error | undefined;
21
+ get exitCode(): number | undefined;
22
+ get failed(): boolean;
23
+ get killed(): boolean;
24
+ get pid(): number | undefined;
25
+ get process(): ChildProcess | undefined;
26
+ get signalCode(): string | undefined;
27
+ kill(signal?: KillSignal): boolean;
28
+ pipe(command: string, args?: readonly string[], options?: Partial<Options>): Subprocess;
29
+ spawn(): void;
30
+ [Symbol.asyncIterator](): AsyncIterator<string>;
31
+ then<TResult1 = ExecResult, TResult2 = never>(onfulfilled?: ((value: ExecResult) => PromiseLike<TResult1> | TResult1) | null, onrejected?: ((reason: unknown) => PromiseLike<TResult2> | TResult2) | null): Promise<TResult1 | TResult2>;
32
+ protected onClose(): void;
33
+ protected onError(error: Error): void;
34
+ protected reset(): void;
35
+ protected toFailure(error: Error): ExecFailure;
36
+ protected toResult(): Promise<ExecResult>;
37
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.exec = exec;
4
+ const exec_process_js_1 = require("./exec-process.cjs");
5
+ function exec(command, args, options) {
6
+ const subprocess = new exec_process_js_1.ExecProcess(command, args, options);
7
+ subprocess.spawn();
8
+ return subprocess;
9
+ }
@@ -0,0 +1,2 @@
1
+ import type { Options, Subprocess } from './types.cjs';
2
+ export declare function exec(command: string, args?: readonly string[] | null, options?: Partial<Options>): Subprocess;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.exec = exports.ExecProcess = void 0;
4
+ var exec_process_js_1 = require("./exec-process.cjs");
5
+ Object.defineProperty(exports, "ExecProcess", { enumerable: true, get: function () { return exec_process_js_1.ExecProcess; } });
6
+ var exec_js_1 = require("./exec.cjs");
7
+ Object.defineProperty(exports, "exec", { enumerable: true, get: function () { return exec_js_1.exec; } });
@@ -0,0 +1,3 @@
1
+ export { ExecProcess } from './exec-process.cjs';
2
+ export { exec } from './exec.cjs';
3
+ export { ExecFailure, ExecResult, ExecSuccess, KillSignal, Options, StdinOption, StdioOption, Subprocess, } from './types.cjs';
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });