spawn-rx 2.0.9 → 3.0.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.
package/src/index.ts CHANGED
@@ -1,336 +1,334 @@
1
- import * as path from 'path';
2
- import * as net from 'net';
3
- import * as sfs from 'fs';
4
- import * as assign from 'lodash.assign';
5
-
6
- import 'rxjs/add/observable/of';
7
- import 'rxjs/add/observable/merge';
8
- import 'rxjs/add/operator/pluck';
9
- import 'rxjs/add/operator/reduce';
10
- import { Observable } from 'rxjs/Observable';
11
- import { Observer } from 'rxjs/Observer';
12
- import { Subscription } from 'rxjs/Subscription';
13
- import { AsyncSubject } from 'rxjs/AsyncSubject';
14
- import { Subject } from 'rxjs/Subject';
15
- import * as childProcess from 'child_process';
16
-
17
- const spawnOg: typeof childProcess.spawn = require('child_process').spawn; //tslint:disable-line:no-var-requires
18
- const isWindows = process.platform === 'win32';
19
-
20
- const d = require('debug')('spawn-rx'); //tslint:disable-line:no-var-requires
21
-
22
- /**
23
- * stat a file but don't throw if it doesn't exist
24
- *
25
- * @param {string} file The path to a file
26
- * @return {Stats} The stats structure
27
- *
28
- * @private
29
- */
30
- function statSyncNoException(file: string): sfs.Stats | null {
31
- try {
32
- return sfs.statSync(file);
33
- } catch (e) {
34
- return null;
35
- }
36
- }
37
-
38
- /**
39
- * Search PATH to see if a file exists in any of the path folders.
40
- *
41
- * @param {string} exe The file to search for
42
- * @return {string} A fully qualified path, or the original path if nothing
43
- * is found
44
- *
45
- * @private
46
- */
47
- function runDownPath(exe: string): string {
48
- // NB: Windows won't search PATH looking for executables in spawn like
49
- // Posix does
50
-
51
- // Files with any directory path don't get this applied
52
- if (exe.match(/[\\\/]/)) {
53
- d('Path has slash in directory, bailing');
54
- return exe;
55
- }
56
-
57
- let target = path.join('.', exe);
58
- if (statSyncNoException(target)) {
59
- d(`Found executable in currect directory: ${target}`);
60
- return target;
61
- }
62
-
63
- let haystack = process.env.PATH.split(isWindows ? ';' : ':');
64
- for (let p of haystack) {
65
- let needle = path.join(p, exe);
66
- if (statSyncNoException(needle)) {
67
- return needle;
68
- };
69
- }
70
-
71
- d('Failed to find executable anywhere in path');
72
- return exe;
73
- }
74
-
75
- /**
76
- * Finds the actual executable and parameters to run on Windows. This method
77
- * mimics the POSIX behavior of being able to run scripts as executables by
78
- * replacing the passed-in executable with the script runner, for PowerShell,
79
- * CMD, and node scripts.
80
- *
81
- * This method also does the work of running down PATH, which spawn on Windows
82
- * also doesn't do, unlike on POSIX.
83
- *
84
- * @param {string} exe The executable to run
85
- * @param {Array<string>} args The arguments to run
86
- *
87
- * @return {Object} The cmd and args to run
88
- * @property {string} cmd The command to pass to spawn
89
- * @property {Array<string>} args The arguments to pass to spawn
90
- */
91
- export function findActualExecutable(exe: string, args: Array<string>): {
92
- cmd: string;
93
- args: Array<string>
94
- } {
95
- // POSIX can just execute scripts directly, no need for silly goosery
96
- if (process.platform !== 'win32') {
97
- return { cmd: runDownPath(exe), args: args };
98
- }
99
-
100
- if (!sfs.existsSync(exe)) {
101
- // NB: When you write something like `surf-client ... -- surf-build` on Windows,
102
- // a shell would normally convert that to surf-build.cmd, but since it's passed
103
- // in as an argument, it doesn't happen
104
- const possibleExts = ['.exe', '.bat', '.cmd', '.ps1'];
105
- for (let ext of possibleExts) {
106
- let possibleFullPath = runDownPath(`${exe}${ext}`);
107
-
108
- if (sfs.existsSync(possibleFullPath)) {
109
- return findActualExecutable(possibleFullPath, args);
110
- }
111
- }
112
- }
113
-
114
- if (exe.match(/\.ps1$/i)) {
115
- let cmd = path.join(process.env.SYSTEMROOT, 'System32', 'WindowsPowerShell', 'v1.0', 'PowerShell.exe');
116
- let psargs = ['-ExecutionPolicy', 'Unrestricted', '-NoLogo', '-NonInteractive', '-File', exe];
117
-
118
- return { cmd: cmd, args: psargs.concat(args) };
119
- }
120
-
121
- if (exe.match(/\.(bat|cmd)$/i)) {
122
- let cmd = path.join(process.env.SYSTEMROOT, 'System32', 'cmd.exe');
123
- let cmdArgs = ['/C', exe, ...args];
124
-
125
- return { cmd: cmd, args: cmdArgs };
126
- }
127
-
128
- if (exe.match(/\.(js)$/i)) {
129
- let cmd = process.execPath;
130
- let nodeArgs = [exe];
131
-
132
- return { cmd: cmd, args: nodeArgs.concat(args) };
133
- }
134
-
135
- // Dunno lol
136
- return { cmd: exe, args: args };
137
- }
138
-
139
- /**
140
- * Spawns a process but detached from the current process. The process is put
141
- * into its own Process Group that can be killed by unsubscribing from the
142
- * return Observable.
143
- *
144
- * @param {string} exe The executable to run
145
- * @param {Array<string>} params The parameters to pass to the child
146
- * @param {Object} opts Options to pass to spawn.
147
- *
148
- * @return {Observable<string>} Returns an Observable that when subscribed
149
- * to, will create a detached process. The
150
- * process output will be streamed to this
151
- * Observable, and if unsubscribed from, the
152
- * process will be terminated early. If the
153
- * process terminates with a non-zero value,
154
- * the Observable will terminate with onError.
155
- */
156
- export function spawnDetached(exe: string, params: Array<string>, opts: any = null): Observable<string> {
157
- const { cmd, args } = findActualExecutable(exe, params);
158
-
159
- if (!isWindows) {
160
- return spawn(cmd, args, assign({}, opts || {}, { detached: true }));
161
- };
162
-
163
- const newParams = [cmd].concat(args);
164
-
165
- let target = path.join(__dirname, '..', 'vendor', 'jobber', 'jobber.exe');
166
- let options = assign({}, opts || {}, { detached: true, jobber: true });
167
-
168
- d(`spawnDetached: ${target}, ${newParams}`);
169
- return spawn(target, newParams, options);
170
- }
171
-
172
- /**
173
- * Spawns a process attached as a child of the current process.
174
- *
175
- * @param {string} exe The executable to run
176
- * @param {Array<string>} params The parameters to pass to the child
177
- * @param {Object} opts Options to pass to spawn.
178
- *
179
- * @return {Observable<string>} Returns an Observable that when subscribed
180
- * to, will create a child process. The
181
- * process output will be streamed to this
182
- * Observable, and if unsubscribed from, the
183
- * process will be terminated early. If the
184
- * process terminates with a non-zero value,
185
- * the Observable will terminate with onError.
186
- */
187
-
188
- export function spawn<T>(exe: string, params: Array<string> = [], opts: any = null): Observable<T|string> {
189
- opts = opts || {};
190
- let spawnObs = Observable.create((subj: Observer<{
191
- source: any,
192
- text: any
193
- }>) => {
194
- let { cmd, args } = findActualExecutable(exe, params);
195
- d(`spawning process: ${cmd} ${args.join()}, ${JSON.stringify(opts)}`);
196
- let origOpts = assign({}, opts);
197
- if ('jobber' in origOpts) {
198
- delete origOpts.jobber;
199
- }
200
- if ('split' in origOpts) {
201
- delete origOpts.split;
202
- };
203
-
204
- const proc = spawnOg(cmd, args, origOpts);
205
-
206
- let bufHandler = (source: string) => (b: string | Buffer) => {
207
- if (b.length < 1) {
208
- return;
209
- };
210
- let chunk = '<< String sent back was too long >>';
211
- try {
212
- chunk = b.toString();
213
- } catch (e) {
214
- chunk = `<< Lost chunk of process output for ${exe} - length was ${b.length}>>`;
215
- }
216
-
217
- subj.next({ source: source, text: chunk });
218
- };
219
-
220
- let ret = new Subscription();
221
-
222
- if (opts.stdin) {
223
- if (proc.stdin) {
224
- ret.add(opts.stdin.subscribe(
225
- (x: any) => proc.stdin.write(x),
226
- subj.error,
227
- () => proc.stdin.end()
228
- ));
229
- } else {
230
- subj.error(new Error(`opts.stdio conflicts with provided spawn opts.stdin observable, 'pipe' is required`));
231
- }
232
- }
233
-
234
- let stderrCompleted: Subject<boolean> | Observable<boolean> | null = null;
235
- let stdoutCompleted: Subject<boolean> | Observable<boolean> | null = null;
236
- let noClose = false;
237
-
238
- if (proc.stdout) {
239
- stdoutCompleted = new AsyncSubject<boolean>();
240
- proc.stdout.on('data', bufHandler('stdout'));
241
- proc.stdout.on('close', () => { (stdoutCompleted! as Subject<boolean>).next(true); (stdoutCompleted! as Subject<boolean>).complete(); });
242
- } else {
243
- stdoutCompleted = Observable.of(true);
244
- }
245
-
246
- if (proc.stderr) {
247
- stderrCompleted = new AsyncSubject<boolean>();
248
- proc.stderr.on('data', bufHandler('stderr'));
249
- proc.stderr.on('close', () => { (stderrCompleted! as Subject<boolean>).next(true); (stderrCompleted! as Subject<boolean>).complete(); });
250
- } else {
251
- stderrCompleted = Observable.of(true);
252
- }
253
-
254
- proc.on('error', (e: Error) => {
255
- noClose = true;
256
- subj.error(e);
257
- });
258
-
259
- proc.on('close', (code: number) => {
260
- noClose = true;
261
- let pipesClosed = Observable.merge(stdoutCompleted!, stderrCompleted!)
262
- .reduce((acc) => acc, true);
263
-
264
- if (code === 0) {
265
- pipesClosed.subscribe(() => subj.complete());
266
- } else {
267
- pipesClosed.subscribe(() => subj.error(new Error(`Failed with exit code: ${code}`)));
268
- }
269
- });
270
-
271
- ret.add(new Subscription(() => {
272
- if (noClose) {
273
- return;
274
- };
275
-
276
- d(`Killing process: ${cmd} ${args.join()}`);
277
- if (opts.jobber) {
278
- // NB: Connecting to Jobber's named pipe will kill it
279
- net.connect(`\\\\.\\pipe\\jobber-${proc.pid}`);
280
- setTimeout(() => proc.kill(), 5 * 1000);
281
- } else {
282
- proc.kill();
283
- }
284
- }));
285
-
286
- return ret;
287
- });
288
-
289
- return opts.split ? spawnObs : spawnObs.pluck('text');
290
- }
291
-
292
- function wrapObservableInPromise<T>(obs: Observable<T>) {
293
- return new Promise((res, rej) => {
294
- let out = '';
295
-
296
- obs.subscribe(
297
- (x) => out += x,
298
- (e) => rej(new Error(`${out}\n${e.message}`)),
299
- () => res(out));
300
- });
301
- }
302
-
303
- /**
304
- * Spawns a process but detached from the current process. The process is put
305
- * into its own Process Group.
306
- *
307
- * @param {string} exe The executable to run
308
- * @param {Array<string>} params The parameters to pass to the child
309
- * @param {Object} opts Options to pass to spawn.
310
- *
311
- * @return {Promise<string>} Returns an Promise that represents a detached
312
- * process. The value returned is the process
313
- * output. If the process terminates with a
314
- * non-zero value, the Promise will resolve with
315
- * an Error.
316
- */
317
- export function spawnDetachedPromise(exe: string, params: Array<string>, opts: any = null): Promise<string> {
318
- return wrapObservableInPromise<string>(spawnDetached(exe, params, opts));
319
- }
320
-
321
- /**
322
- * Spawns a process as a child process.
323
- *
324
- * @param {string} exe The executable to run
325
- * @param {Array<string>} params The parameters to pass to the child
326
- * @param {Object} opts Options to pass to spawn.
327
- *
328
- * @return {Promise<string>} Returns an Promise that represents a child
329
- * process. The value returned is the process
330
- * output. If the process terminates with a
331
- * non-zero value, the Promise will resolve with
332
- * an Error.
333
- */
334
- export function spawnPromise(exe: string, params: Array<string>, opts: any = null): Promise<string> {
335
- return wrapObservableInPromise<string>(spawn(exe, params, opts));
336
- }
1
+ import * as path from 'path';
2
+ import * as net from 'net';
3
+ import * as sfs from 'fs';
4
+ import * as assign from 'lodash.assign';
5
+
6
+ import { Observable, Observer, Subscription, AsyncSubject, Subject, of, merge } from 'rxjs';
7
+ import { pluck, reduce } from 'rxjs/operators';
8
+ import * as childProcess from 'child_process';
9
+
10
+ const spawnOg: typeof childProcess.spawn = require('child_process').spawn; //tslint:disable-line:no-var-requires
11
+ const isWindows = process.platform === 'win32';
12
+
13
+ const d = require('debug')('spawn-rx'); //tslint:disable-line:no-var-requires
14
+
15
+ /**
16
+ * stat a file but don't throw if it doesn't exist
17
+ *
18
+ * @param {string} file The path to a file
19
+ * @return {Stats} The stats structure
20
+ *
21
+ * @private
22
+ */
23
+ function statSyncNoException(file: string): sfs.Stats | null {
24
+ try {
25
+ return sfs.statSync(file);
26
+ } catch (e) {
27
+ return null;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Search PATH to see if a file exists in any of the path folders.
33
+ *
34
+ * @param {string} exe The file to search for
35
+ * @return {string} A fully qualified path, or the original path if nothing
36
+ * is found
37
+ *
38
+ * @private
39
+ */
40
+ function runDownPath(exe: string): string {
41
+ // NB: Windows won't search PATH looking for executables in spawn like
42
+ // Posix does
43
+
44
+ // Files with any directory path don't get this applied
45
+ if (exe.match(/[\\\/]/)) {
46
+ d('Path has slash in directory, bailing');
47
+ return exe;
48
+ }
49
+
50
+ let target = path.join('.', exe);
51
+ if (statSyncNoException(target)) {
52
+ d(`Found executable in currect directory: ${target}`);
53
+ return target;
54
+ }
55
+
56
+ let haystack = process.env.PATH!.split(isWindows ? ';' : ':');
57
+ for (let p of haystack) {
58
+ let needle = path.join(p, exe);
59
+ if (statSyncNoException(needle)) {
60
+ return needle;
61
+ }
62
+ }
63
+
64
+ d('Failed to find executable anywhere in path');
65
+ return exe;
66
+ }
67
+
68
+ /**
69
+ * Finds the actual executable and parameters to run on Windows. This method
70
+ * mimics the POSIX behavior of being able to run scripts as executables by
71
+ * replacing the passed-in executable with the script runner, for PowerShell,
72
+ * CMD, and node scripts.
73
+ *
74
+ * This method also does the work of running down PATH, which spawn on Windows
75
+ * also doesn't do, unlike on POSIX.
76
+ *
77
+ * @param {string} exe The executable to run
78
+ * @param {Array<string>} args The arguments to run
79
+ *
80
+ * @return {Object} The cmd and args to run
81
+ * @property {string} cmd The command to pass to spawn
82
+ * @property {Array<string>} args The arguments to pass to spawn
83
+ */
84
+ export function findActualExecutable(exe: string, args: Array<string>): {
85
+ cmd: string;
86
+ args: Array<string>
87
+ } {
88
+ // POSIX can just execute scripts directly, no need for silly goosery
89
+ if (process.platform !== 'win32') {
90
+ return { cmd: runDownPath(exe), args: args };
91
+ }
92
+
93
+ if (!sfs.existsSync(exe)) {
94
+ // NB: When you write something like `surf-client ... -- surf-build` on Windows,
95
+ // a shell would normally convert that to surf-build.cmd, but since it's passed
96
+ // in as an argument, it doesn't happen
97
+ const possibleExts = ['.exe', '.bat', '.cmd', '.ps1'];
98
+ for (let ext of possibleExts) {
99
+ let possibleFullPath = runDownPath(`${exe}${ext}`);
100
+
101
+ if (sfs.existsSync(possibleFullPath)) {
102
+ return findActualExecutable(possibleFullPath, args);
103
+ }
104
+ }
105
+ }
106
+
107
+ if (exe.match(/\.ps1$/i)) {
108
+ let cmd = path.join(process.env.SYSTEMROOT!, 'System32', 'WindowsPowerShell', 'v1.0', 'PowerShell.exe');
109
+ let psargs = ['-ExecutionPolicy', 'Unrestricted', '-NoLogo', '-NonInteractive', '-File', exe];
110
+
111
+ return { cmd: cmd, args: psargs.concat(args) };
112
+ }
113
+
114
+ if (exe.match(/\.(bat|cmd)$/i)) {
115
+ let cmd = path.join(process.env.SYSTEMROOT!, 'System32', 'cmd.exe');
116
+ let cmdArgs = ['/C', exe, ...args];
117
+
118
+ return { cmd: cmd, args: cmdArgs };
119
+ }
120
+
121
+ if (exe.match(/\.(js)$/i)) {
122
+ let cmd = process.execPath;
123
+ let nodeArgs = [exe];
124
+
125
+ return { cmd: cmd, args: nodeArgs.concat(args) };
126
+ }
127
+
128
+ // Dunno lol
129
+ return { cmd: exe, args: args };
130
+ }
131
+
132
+ /**
133
+ * Spawns a process but detached from the current process. The process is put
134
+ * into its own Process Group that can be killed by unsubscribing from the
135
+ * return Observable.
136
+ *
137
+ * @param {string} exe The executable to run
138
+ * @param {Array<string>} params The parameters to pass to the child
139
+ * @param {Object} opts Options to pass to spawn.
140
+ *
141
+ * @return {Observable<string>} Returns an Observable that when subscribed
142
+ * to, will create a detached process. The
143
+ * process output will be streamed to this
144
+ * Observable, and if unsubscribed from, the
145
+ * process will be terminated early. If the
146
+ * process terminates with a non-zero value,
147
+ * the Observable will terminate with onError.
148
+ */
149
+ export function spawnDetached(exe: string, params: Array<string>, opts: any = null): Observable<string> {
150
+ const { cmd, args } = findActualExecutable(exe, params);
151
+
152
+ if (!isWindows) {
153
+ return spawn(cmd, args, assign({}, opts || {}, { detached: true }));
154
+ }
155
+
156
+ const newParams = [cmd].concat(args);
157
+
158
+ let target = path.join(__dirname, '..', '..', 'vendor', 'jobber', 'Jobber.exe');
159
+ let options = assign({}, opts || {}, { detached: true, jobber: true });
160
+
161
+ d(`spawnDetached: ${target}, ${newParams}`);
162
+ return spawn(target, newParams, options);
163
+ }
164
+
165
+ /**
166
+ * Spawns a process attached as a child of the current process.
167
+ *
168
+ * @param {string} exe The executable to run
169
+ * @param {Array<string>} params The parameters to pass to the child
170
+ * @param {Object} opts Options to pass to spawn.
171
+ *
172
+ * @return {Observable<string>} Returns an Observable that when subscribed
173
+ * to, will create a child process. The
174
+ * process output will be streamed to this
175
+ * Observable, and if unsubscribed from, the
176
+ * process will be terminated early. If the
177
+ * process terminates with a non-zero value,
178
+ * the Observable will terminate with onError.
179
+ */
180
+
181
+ export function spawn<T = string>(exe: string, params: Array<string> = [], opts: any = null): Observable<T> {
182
+ opts = opts || {};
183
+ let spawnObs = Observable.create((subj: Observer<{
184
+ source: any,
185
+ text: any
186
+ }>) => {
187
+ let { stdin, ...optsWithoutStdIn } = opts;
188
+ let { cmd, args } = findActualExecutable(exe, params);
189
+ d(`spawning process: ${cmd} ${args.join()}, ${JSON.stringify(optsWithoutStdIn)}`);
190
+ let origOpts = assign({}, optsWithoutStdIn);
191
+ if ('jobber' in origOpts) {
192
+ delete origOpts.jobber;
193
+ }
194
+ if ('split' in origOpts) {
195
+ delete origOpts.split;
196
+ }
197
+
198
+ const proc = spawnOg(cmd, args, origOpts);
199
+
200
+ let bufHandler = (source: string) => (b: string | Buffer) => {
201
+ if (b.length < 1) {
202
+ return;
203
+ }
204
+ let chunk = '<< String sent back was too long >>';
205
+ try {
206
+ if (typeof b === 'string') {
207
+ chunk = b.toString();
208
+ } else {
209
+ chunk = b.toString(origOpts.encoding || 'utf8');
210
+ }
211
+ } catch (e) {
212
+ chunk = `<< Lost chunk of process output for ${exe} - length was ${b.length}>>`;
213
+ }
214
+
215
+ subj.next({ source: source, text: chunk });
216
+ };
217
+
218
+ let ret = new Subscription();
219
+
220
+ if (opts.stdin) {
221
+ if (proc.stdin) {
222
+ ret.add(opts.stdin.subscribe(
223
+ (x: any) => proc.stdin.write(x),
224
+ subj.error.bind(subj),
225
+ () => proc.stdin.end()
226
+ ));
227
+ } else {
228
+ subj.error(new Error(`opts.stdio conflicts with provided spawn opts.stdin observable, 'pipe' is required`));
229
+ }
230
+ }
231
+
232
+ let stderrCompleted: Subject<boolean> | Observable<boolean> | null = null;
233
+ let stdoutCompleted: Subject<boolean> | Observable<boolean> | null = null;
234
+ let noClose = false;
235
+
236
+ if (proc.stdout) {
237
+ stdoutCompleted = new AsyncSubject<boolean>();
238
+ proc.stdout.on('data', bufHandler('stdout'));
239
+ proc.stdout.on('close', () => { (stdoutCompleted! as Subject<boolean>).next(true); (stdoutCompleted! as Subject<boolean>).complete(); });
240
+ } else {
241
+ stdoutCompleted = of(true);
242
+ }
243
+
244
+ if (proc.stderr) {
245
+ stderrCompleted = new AsyncSubject<boolean>();
246
+ proc.stderr.on('data', bufHandler('stderr'));
247
+ proc.stderr.on('close', () => { (stderrCompleted! as Subject<boolean>).next(true); (stderrCompleted! as Subject<boolean>).complete(); });
248
+ } else {
249
+ stderrCompleted = of(true);
250
+ }
251
+
252
+ proc.on('error', (e: Error) => {
253
+ noClose = true;
254
+ subj.error(e);
255
+ });
256
+
257
+ proc.on('close', (code: number) => {
258
+ noClose = true;
259
+ let pipesClosed = merge(stdoutCompleted!, stderrCompleted!)
260
+ .pipe(reduce((acc) => acc, true));
261
+
262
+ if (code === 0) {
263
+ pipesClosed.subscribe(() => subj.complete());
264
+ } else {
265
+ pipesClosed.subscribe(() => subj.error(new Error(`Failed with exit code: ${code}`)));
266
+ }
267
+ });
268
+
269
+ ret.add(new Subscription(() => {
270
+ if (noClose) {
271
+ return;
272
+ }
273
+
274
+ d(`Killing process: ${cmd} ${args.join()}`);
275
+ if (opts.jobber) {
276
+ // NB: Connecting to Jobber's named pipe will kill it
277
+ net.connect(`\\\\.\\pipe\\jobber-${proc.pid}`);
278
+ setTimeout(() => proc.kill(), 5 * 1000);
279
+ } else {
280
+ proc.kill();
281
+ }
282
+ }));
283
+
284
+ return ret;
285
+ });
286
+
287
+ return opts.split ? spawnObs : spawnObs.pipe(pluck('text'));
288
+ }
289
+
290
+ function wrapObservableInPromise<T>(obs: Observable<T>) {
291
+ return new Promise<string>((res, rej) => {
292
+ let out = '';
293
+
294
+ obs.subscribe(
295
+ (x) => out += x,
296
+ (e) => rej(new Error(`${out}\n${e.message}`)),
297
+ () => res(out));
298
+ });
299
+ }
300
+
301
+ /**
302
+ * Spawns a process but detached from the current process. The process is put
303
+ * into its own Process Group.
304
+ *
305
+ * @param {string} exe The executable to run
306
+ * @param {Array<string>} params The parameters to pass to the child
307
+ * @param {Object} opts Options to pass to spawn.
308
+ *
309
+ * @return {Promise<string>} Returns an Promise that represents a detached
310
+ * process. The value returned is the process
311
+ * output. If the process terminates with a
312
+ * non-zero value, the Promise will resolve with
313
+ * an Error.
314
+ */
315
+ export function spawnDetachedPromise(exe: string, params: Array<string>, opts: any = null): Promise<string> {
316
+ return wrapObservableInPromise<string>(spawnDetached(exe, params, opts));
317
+ }
318
+
319
+ /**
320
+ * Spawns a process as a child process.
321
+ *
322
+ * @param {string} exe The executable to run
323
+ * @param {Array<string>} params The parameters to pass to the child
324
+ * @param {Object} opts Options to pass to spawn.
325
+ *
326
+ * @return {Promise<string>} Returns an Promise that represents a child
327
+ * process. The value returned is the process
328
+ * output. If the process terminates with a
329
+ * non-zero value, the Promise will resolve with
330
+ * an Error.
331
+ */
332
+ export function spawnPromise(exe: string, params: Array<string>, opts: any = null): Promise<string> {
333
+ return wrapObservableInPromise<string>(spawn(exe, params, opts));
334
+ }