@zokugun/exec-lite 0.0.0-stage → 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 +275 -2
  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 +123 -5
@@ -0,0 +1,36 @@
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.detectShebang = detectShebang;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = require("node:path");
9
+ const SHEBANG_REGEX = /^#!\s*(.+)/;
10
+ const SIZE = 150;
11
+ function detectShebang(file) {
12
+ const buffer = Buffer.alloc(SIZE);
13
+ let fd = null;
14
+ try {
15
+ fd = node_fs_1.default.openSync(file, 'r');
16
+ node_fs_1.default.readSync(fd, buffer, 0, SIZE, 0);
17
+ }
18
+ catch {
19
+ // do nothing, we'll just assume it's not a shebang
20
+ }
21
+ finally {
22
+ if (fd !== null) {
23
+ node_fs_1.default.closeSync(fd);
24
+ }
25
+ }
26
+ const match = SHEBANG_REGEX.exec(buffer.toString());
27
+ if (match === null) {
28
+ return null;
29
+ }
30
+ const line = match[1].trim();
31
+ const separatorIndex = line.indexOf(' ');
32
+ const path = separatorIndex !== -1 ? line.slice(0, separatorIndex) : line;
33
+ const argument = separatorIndex !== -1 ? line.slice(separatorIndex + 1) : '';
34
+ const binary = (0, node_path_1.basename)(path);
35
+ return binary === 'env' ? argument || null : binary;
36
+ }
@@ -0,0 +1 @@
1
+ export declare function detectShebang(file: string): string | null;
@@ -0,0 +1,59 @@
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.normalizeCommand = normalizeCommand;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const detectShebang_js_1 = require("./detectShebang.cjs");
9
+ const resolveCommand_js_1 = require("./resolveCommand.cjs");
10
+ const IS_BATCH_FILE_REGEX = /\.(?:bat|cmd)$/i;
11
+ const IS_EXECUTABLE_REGEX = /\.(?:com|exe)$/i;
12
+ const IS_WINDOWS = process.platform === 'win32';
13
+ const META_CHARS_REGEX = /([ !"%&()*,;<>?[\]^`|])/g;
14
+ function normalizeCommand(command, args, options) {
15
+ if (['node', 'node.exe'].includes(command.toLowerCase())) {
16
+ command = process.execPath;
17
+ args = [...process.execArgv.filter((flag) => !flag.startsWith('--inspect')), ...args];
18
+ }
19
+ if (options.shell === true || !IS_WINDOWS) {
20
+ return {
21
+ command,
22
+ args,
23
+ options,
24
+ };
25
+ }
26
+ let file = (0, resolveCommand_js_1.resolveCommand)(command, options);
27
+ if (file !== null) {
28
+ const shebang = (0, detectShebang_js_1.detectShebang)(file);
29
+ if (shebang !== null) {
30
+ args = [file, ...args];
31
+ command = shebang;
32
+ file = (0, resolveCommand_js_1.resolveCommand)(command, options);
33
+ }
34
+ }
35
+ if (file !== null && IS_EXECUTABLE_REGEX.test(file)) {
36
+ return {
37
+ command,
38
+ args,
39
+ options,
40
+ };
41
+ }
42
+ const needsDoubleEscape = file !== null && IS_BATCH_FILE_REGEX.test(file);
43
+ const normalizedCommand = node_path_1.default.normalize(command).replace(META_CHARS_REGEX, '^$1');
44
+ const normalizedArgs = [];
45
+ for (let arg of args) {
46
+ arg = arg.replace(/(?=(\\+?))\1(?:"|$)/g, '$1$1\\"');
47
+ arg = `"${arg}"`;
48
+ arg = arg.replace(META_CHARS_REGEX, '^$1');
49
+ if (needsDoubleEscape) {
50
+ arg = arg.replace(META_CHARS_REGEX, '^$1');
51
+ }
52
+ normalizedArgs.push(arg);
53
+ }
54
+ return {
55
+ command: options.env?.comspec ?? 'cmd.exe',
56
+ args: ['/d', '/s', '/c', `"${[normalizedCommand, ...normalizedArgs].join(' ')}"`],
57
+ options: { ...options, windowsVerbatimArguments: true },
58
+ };
59
+ }
@@ -0,0 +1,6 @@
1
+ import type { SpawnOptions } from '../../types.cjs';
2
+ export declare function normalizeCommand(command: string, args: readonly string[], options: SpawnOptions): {
3
+ args: readonly string[];
4
+ command: string;
5
+ options: SpawnOptions;
6
+ };
@@ -0,0 +1,43 @@
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.resolveCommand = resolveCommand;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const getPathFromEnv_js_1 = require("../env/getPathFromEnv.cjs");
10
+ const CACHE = Object.create(null);
11
+ const DEFAULT_PATH_EXT = ['.EXE', '.CMD', '.BAT', '.COM'];
12
+ function resolveCommand(command, options) {
13
+ const cwd = options.cwd ?? process.cwd();
14
+ const env = options.env ?? process.env;
15
+ const pathEnv = (0, getPathFromEnv_js_1.getPathFromEnv)(env).value;
16
+ const key = `${command}%%%%${pathEnv}`;
17
+ if (CACHE[key] !== undefined) {
18
+ return CACHE[key];
19
+ }
20
+ const parts = command.includes('/') || command.includes('\\') ? [''] : [cwd, ...pathEnv.split(node_path_1.default.delimiter)];
21
+ let pathExt = env.PATHEXT ? env.PATHEXT.split(node_path_1.default.delimiter) : DEFAULT_PATH_EXT;
22
+ if (command.includes('.') && pathExt[0] !== '') {
23
+ pathExt = ['', ...pathExt];
24
+ }
25
+ for (const part of parts) {
26
+ const unquoted = part.startsWith('"') && part.endsWith('"') && part.length > 1 ? part.slice(1, -1) : part;
27
+ const dest = node_path_1.default.resolve(cwd, unquoted, command);
28
+ for (const ext of [...pathExt, '']) {
29
+ const destWithExt = dest + ext;
30
+ try {
31
+ if (node_fs_1.default.statSync(destWithExt).isFile()) {
32
+ CACHE[key] = destWithExt;
33
+ return destWithExt;
34
+ }
35
+ }
36
+ catch {
37
+ // do nothing, it didn't exist
38
+ }
39
+ }
40
+ }
41
+ CACHE[key] = null;
42
+ return null;
43
+ }
@@ -0,0 +1,2 @@
1
+ import type { SpawnOptions } from '../../types.cjs';
2
+ export declare function resolveCommand(command: string, options: SpawnOptions): string | null;
@@ -0,0 +1,24 @@
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.addNodeBinToPath = addNodeBinToPath;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ function addNodeBinToPath(cwd, { value, key }) {
9
+ const parts = value.split(node_path_1.default.delimiter);
10
+ const nodeBinPaths = [];
11
+ let currentPath = cwd;
12
+ let lastPath;
13
+ do {
14
+ nodeBinPaths.push(node_path_1.default.resolve(currentPath, 'node_modules', '.bin'));
15
+ lastPath = currentPath;
16
+ currentPath = node_path_1.default.dirname(currentPath);
17
+ } while (currentPath !== lastPath);
18
+ nodeBinPaths.push(node_path_1.default.dirname(process.execPath));
19
+ const newValue = nodeBinPaths.concat(parts).join(node_path_1.default.delimiter);
20
+ return {
21
+ key,
22
+ value: newValue,
23
+ };
24
+ }
@@ -0,0 +1,2 @@
1
+ import type { EnvPathInfo } from './types.cjs';
2
+ export declare function addNodeBinToPath(cwd: string, { value, key }: EnvPathInfo): EnvPathInfo;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeEnv = computeEnv;
4
+ const addNodeBinToPath_js_1 = require("./addNodeBinToPath.cjs");
5
+ const getPathFromEnv_js_1 = require("./getPathFromEnv.cjs");
6
+ function computeEnv(env, preferLocal, localDir) {
7
+ const computedEnv = {
8
+ ...process.env,
9
+ ...env,
10
+ };
11
+ if (!preferLocal) {
12
+ return computedEnv;
13
+ }
14
+ const pathEnvInfo = (0, addNodeBinToPath_js_1.addNodeBinToPath)(localDir, (0, getPathFromEnv_js_1.getPathFromEnv)(computedEnv));
15
+ computedEnv[pathEnvInfo.key] = pathEnvInfo.value;
16
+ return computedEnv;
17
+ }
@@ -0,0 +1,2 @@
1
+ import type { EnvLike } from './types.cjs';
2
+ export declare function computeEnv(env: Readonly<EnvLike> | undefined, preferLocal: boolean, localDir: string): Readonly<EnvLike>;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPathFromEnv = getPathFromEnv;
4
+ function getPathFromEnv(env) {
5
+ for (const key in env) {
6
+ if (key.toLowerCase() !== 'path') {
7
+ continue;
8
+ }
9
+ const value = env[key];
10
+ if (!value) {
11
+ return {
12
+ key: 'PATH',
13
+ value: '',
14
+ };
15
+ }
16
+ return { key, value };
17
+ }
18
+ return {
19
+ key: 'PATH',
20
+ value: '',
21
+ };
22
+ }
@@ -0,0 +1,2 @@
1
+ import type { EnvLike, EnvPathInfo } from './types.cjs';
2
+ export declare function getPathFromEnv(env: EnvLike): EnvPathInfo;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ export type EnvLike = (typeof process)['env'];
2
+ export type EnvPathInfo = {
3
+ key: string;
4
+ value: string;
5
+ };
@@ -0,0 +1,31 @@
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.normalizeOptions = normalizeOptions;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const computeEnv_js_1 = require("./env/computeEnv.cjs");
9
+ const combineSignals_js_1 = require("../combineSignals.cjs");
10
+ function normalizeOptions(options) {
11
+ const { cwd, env, preferLocal, signal, stderr, stdin, stdio: stdioOption, stdout, ...restOptions } = options;
12
+ const spawnOptions = restOptions;
13
+ spawnOptions.cwd = node_path_1.default.resolve(cwd ?? process.cwd());
14
+ spawnOptions.env = (0, computeEnv_js_1.computeEnv)(env, preferLocal ?? false, options.localDir ?? spawnOptions.cwd);
15
+ const signals = [];
16
+ if (options.timeout !== undefined) {
17
+ signals.push(AbortSignal.timeout(options.timeout));
18
+ }
19
+ if (signal !== undefined) {
20
+ signals.push(signal);
21
+ }
22
+ if (signals.length > 0) {
23
+ spawnOptions.signal = (0, combineSignals_js_1.combineSignals)(signals);
24
+ }
25
+ const stdio = stdioOption ?? [undefined, stdout, stderr];
26
+ if (Array.isArray(stdio) && !(typeof stdin === 'string' || (typeof stdin === 'object' && stdin !== null && 'process' in stdin))) {
27
+ stdio[0] = 'pipe';
28
+ }
29
+ spawnOptions.stdio = stdio;
30
+ return spawnOptions;
31
+ }
@@ -0,0 +1,2 @@
1
+ import type { Options, SpawnOptions } from '../types.cjs';
2
+ export declare function normalizeOptions(options: Options): SpawnOptions;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.combineStreams = combineStreams;
4
+ const node_stream_1 = require("node:stream");
5
+ const promises_1 = require("node:stream/promises");
6
+ function combineStreams(streams) {
7
+ let streamCount = streams.length;
8
+ const combined = new node_stream_1.PassThrough();
9
+ if (streamCount === 0) {
10
+ combined.end();
11
+ return combined;
12
+ }
13
+ const maybeEmitEnd = () => {
14
+ streamCount -= 1;
15
+ if (streamCount === 0) {
16
+ combined.end();
17
+ }
18
+ };
19
+ for (const stream of streams) {
20
+ (0, promises_1.pipeline)(stream, combined, { end: false })
21
+ .then(maybeEmitEnd)
22
+ .catch(maybeEmitEnd);
23
+ }
24
+ return combined;
25
+ }
@@ -0,0 +1,2 @@
1
+ import type { Readable } from 'node:stream';
2
+ export declare function combineStreams(streams: Readable[]): Readable;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readStream = readStream;
4
+ async function readStream(stream, context) {
5
+ let output = '';
6
+ try {
7
+ for await (const chunk of stream) {
8
+ output += String(chunk);
9
+ context.all += String(chunk);
10
+ }
11
+ }
12
+ catch {
13
+ // suppress errors, child process probably failed to spawn
14
+ }
15
+ return output;
16
+ }
@@ -0,0 +1,5 @@
1
+ import type { Readable } from 'node:stream';
2
+ export type StreamContext = {
3
+ all: string;
4
+ };
5
+ export declare function readStream(stream: Readable, context: StreamContext): Promise<string>;
@@ -0,0 +1 @@
1
+ export declare function combineSignals(signals: AbortSignal[]): AbortSignal;
@@ -0,0 +1,16 @@
1
+ export function combineSignals(signals) {
2
+ const controller = new AbortController();
3
+ for (const signal of signals) {
4
+ if (signal.aborted) {
5
+ controller.abort();
6
+ return signal;
7
+ }
8
+ const onAbort = () => {
9
+ controller.abort(signal.reason);
10
+ };
11
+ signal.addEventListener('abort', onAbort, {
12
+ signal: controller.signal,
13
+ });
14
+ }
15
+ return controller.signal;
16
+ }
@@ -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.mjs';
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,214 @@
1
+ import { spawn } from 'node:child_process';
2
+ import readline from 'node:readline';
3
+ import { err, ok } from '@zokugun/xtry';
4
+ import { exec } from './exec.mjs';
5
+ import { normalizeCommand } from './utils/command/normalizeCommand.mjs';
6
+ import { normalizeOptions } from './utils/normalizeOptions.mjs';
7
+ import { combineStreams } from './utils/stream/combineStreams.mjs';
8
+ import { readStream } from './utils/stream/readStream.mjs';
9
+ export class ExecProcess {
10
+ _aborted = false;
11
+ _args;
12
+ _command;
13
+ _failed = false;
14
+ _options;
15
+ _process;
16
+ _processClosed;
17
+ _resolveClose;
18
+ _signal;
19
+ _start;
20
+ _streamErr;
21
+ _streamOut;
22
+ _thrownError;
23
+ constructor(command, args, options) {
24
+ this._options = {
25
+ timeout: undefined,
26
+ windowsHide: true,
27
+ ...options,
28
+ };
29
+ this._command = command;
30
+ this._args = args ?? [];
31
+ this._processClosed = new Promise((resolve) => {
32
+ this._resolveClose = resolve;
33
+ });
34
+ } // }}}
35
+ get aborted() {
36
+ return this._aborted;
37
+ } // }}}
38
+ get error() {
39
+ return this._thrownError;
40
+ } // }}}
41
+ get exitCode() {
42
+ return this._process?.exitCode ?? undefined;
43
+ } // }}}
44
+ get failed() {
45
+ return this._failed;
46
+ } // }}}
47
+ get killed() {
48
+ return this._process?.killed === true;
49
+ } // }}}
50
+ get pid() {
51
+ return this._process?.pid;
52
+ } // }}}
53
+ get process() {
54
+ return this._process;
55
+ } // }}}
56
+ get signalCode() {
57
+ return this._process?.signalCode ?? undefined;
58
+ } // }}}
59
+ kill(signal) {
60
+ return this._process?.kill(signal) === true;
61
+ } // }}}
62
+ pipe(command, args, options) {
63
+ return exec(command, args, {
64
+ ...options,
65
+ stdin: this,
66
+ });
67
+ } // }}}
68
+ spawn() {
69
+ this._start = process.hrtime.bigint();
70
+ this.reset();
71
+ const normalizedOptions = normalizeOptions(this._options);
72
+ if (normalizedOptions.signal) {
73
+ this._signal = normalizedOptions.signal;
74
+ }
75
+ const { command: spawnCommand, args: spawnArgs, options: spawnOptions } = normalizeCommand(this._command, this._args, normalizedOptions);
76
+ let task;
77
+ try {
78
+ task = spawn(spawnCommand, spawnArgs, spawnOptions);
79
+ }
80
+ catch (error) {
81
+ if (error instanceof Error) {
82
+ this._thrownError = error;
83
+ }
84
+ else {
85
+ this._thrownError = new Error(String(error));
86
+ }
87
+ return;
88
+ }
89
+ if (task.stderr) {
90
+ this._streamErr = task.stderr;
91
+ }
92
+ if (task.stdout) {
93
+ this._streamOut = task.stdout;
94
+ }
95
+ this._process = task;
96
+ task.once('error', this.onError.bind(this));
97
+ task.once('close', this.onClose.bind(this));
98
+ if (task.stdin) {
99
+ const { stdin } = this._options;
100
+ if (typeof stdin === 'string') {
101
+ task.stdin.end(stdin);
102
+ }
103
+ else if (typeof stdin === 'object' && stdin !== null && 'process' in stdin) {
104
+ stdin.process?.stdout?.pipe(task.stdin);
105
+ }
106
+ }
107
+ } // }}}
108
+ // eslint-disable-next-line ts/explicit-member-accessibility
109
+ async *[Symbol.asyncIterator]() {
110
+ const handle = this._process;
111
+ if (!handle) {
112
+ return;
113
+ }
114
+ const streams = [];
115
+ if (this._streamErr) {
116
+ streams.push(this._streamErr);
117
+ }
118
+ if (this._streamOut) {
119
+ streams.push(this._streamOut);
120
+ }
121
+ const streamCombined = combineStreams(streams);
122
+ const rl = readline.createInterface({ input: streamCombined });
123
+ for await (const chunk of rl) {
124
+ // eslint-disable-next-line ts/no-unnecessary-type-conversion
125
+ yield chunk.toString();
126
+ }
127
+ await this._processClosed;
128
+ handle.removeAllListeners();
129
+ if (this._thrownError || (this.exitCode !== 0 && this.exitCode !== undefined) || this.signalCode !== undefined) {
130
+ this._failed = true;
131
+ }
132
+ } // }}}
133
+ // eslint-disable-next-line ts/promise-function-async
134
+ then(onfulfilled, onrejected) {
135
+ return this.toResult().then(onfulfilled, onrejected);
136
+ } // }}}
137
+ onClose() {
138
+ if (this._resolveClose) {
139
+ this._resolveClose();
140
+ }
141
+ } // }}}
142
+ onError(error) {
143
+ if (error.name === 'AbortError' && (!(error.cause instanceof Error) || error.cause.name !== 'TimeoutError')) {
144
+ this._aborted = true;
145
+ return;
146
+ }
147
+ this._thrownError = error;
148
+ } // }}}
149
+ reset() {
150
+ this._aborted = false;
151
+ this._process = undefined;
152
+ this._processClosed = new Promise((resolve) => {
153
+ this._resolveClose = resolve;
154
+ });
155
+ this._signal = undefined;
156
+ this._streamErr = undefined;
157
+ this._streamOut = undefined;
158
+ this._thrownError = undefined;
159
+ } // }}}
160
+ toFailure(error) {
161
+ this._failed = true;
162
+ const result = {
163
+ all: '',
164
+ canceled: this._signal?.aborted ?? false,
165
+ command: this._command,
166
+ durationMs: Number(process.hrtime.bigint() - this._start) / 1e6,
167
+ error,
168
+ exitCode: this.exitCode,
169
+ signalCode: this.signalCode,
170
+ };
171
+ return result;
172
+ } // }}}
173
+ async toResult() {
174
+ if (this._thrownError) {
175
+ return err(this.toFailure(this._thrownError));
176
+ }
177
+ const task = this._process;
178
+ if (!task) {
179
+ return err(this.toFailure(new Error('No process was started')));
180
+ }
181
+ const context = { all: '' };
182
+ const [stdout, stderr] = await Promise.all([
183
+ this._streamOut ? readStream(this._streamOut, context) : '',
184
+ this._streamErr ? readStream(this._streamErr, context) : '',
185
+ ]);
186
+ await this._processClosed;
187
+ const { stdin } = this._options;
188
+ if (stdin && typeof stdin !== 'string') {
189
+ await stdin;
190
+ }
191
+ task.removeAllListeners();
192
+ if (this._thrownError) {
193
+ return err(this.toFailure(this._thrownError));
194
+ }
195
+ const result = {
196
+ all: context.all,
197
+ command: this._command,
198
+ durationMs: Number(process.hrtime.bigint() - this._start) / 1e6,
199
+ exitCode: this.exitCode,
200
+ stderr,
201
+ stdout,
202
+ };
203
+ // if(this._options.stdin instanceof ExecProcess) {
204
+ // result.pipedFrom = this._options.stdin;
205
+ // }
206
+ if ((this.exitCode !== 0 && this.exitCode !== undefined) || this.signalCode !== undefined) {
207
+ this._failed = true;
208
+ const failure = result;
209
+ failure.signalCode = this.signalCode;
210
+ return err(failure);
211
+ }
212
+ return ok(result);
213
+ } // }}}
214
+ }
@@ -0,0 +1,2 @@
1
+ import type { Options, Subprocess } from './types.mjs';
2
+ export declare function exec(command: string, args?: readonly string[] | null, options?: Partial<Options>): Subprocess;
@@ -0,0 +1,6 @@
1
+ import { ExecProcess } from './exec-process.mjs';
2
+ export function exec(command, args, options) {
3
+ const subprocess = new ExecProcess(command, args, options);
4
+ subprocess.spawn();
5
+ return subprocess;
6
+ }
@@ -0,0 +1,3 @@
1
+ export { ExecProcess } from './exec-process.mjs';
2
+ export { exec } from './exec.mjs';
3
+ export { ExecFailure, ExecResult, ExecSuccess, KillSignal, Options, StdinOption, StdioOption, Subprocess, } from './types.mjs';
@@ -0,0 +1,2 @@
1
+ export { ExecProcess } from './exec-process.mjs';
2
+ export { exec } from './exec.mjs';