@james-pre/config 0.0.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,351 +0,0 @@
1
- var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
- var useValue = arguments.length > 2;
3
- for (var i = 0; i < initializers.length; i++) {
4
- value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
- }
6
- return useValue ? value : void 0;
7
- };
8
- var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
- function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
- var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
- var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
- var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
- var _, done = false;
14
- for (var i = decorators.length - 1; i >= 0; i--) {
15
- var context = {};
16
- for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
- for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
- context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
- var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
- if (kind === "accessor") {
21
- if (result === void 0) continue;
22
- if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
- if (_ = accept(result.get)) descriptor.get = _;
24
- if (_ = accept(result.set)) descriptor.set = _;
25
- if (_ = accept(result.init)) initializers.unshift(_);
26
- }
27
- else if (_ = accept(result)) {
28
- if (kind === "field") initializers.unshift(_);
29
- else descriptor[key] = _;
30
- }
31
- }
32
- if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
- done = true;
34
- };
35
- // SPDX-License-Identifier: LGPL-3.0-or-later
36
- import EventEmitter from 'node:events';
37
- import { mkdirSync, readFileSync } from 'node:fs';
38
- import { homedir } from 'node:os';
39
- import { dirname, join, resolve, sep } from 'node:path';
40
- import { deepAssign, getByString, memoize, setByString } from 'utilium';
41
- import * as z from 'zod';
42
- import { debug, info, warn, writeJSON } from './core.js';
43
- const kInit = Symbol.for('LoadOptions:init');
44
- const include = z.string().array().optional();
45
- function canInclude(options, file) {
46
- return (!!options.enableIncludes
47
- && typeof file === 'object'
48
- && file !== null
49
- && 'include' in file
50
- && Array.isArray(file.include));
51
- }
52
- const xdgConfigDir = process.env.XDG_CONFIG_HOME
53
- || (process.platform == 'win32' ? process.env.APPDATA : null)
54
- || join(homedir(), '.config');
55
- const systemConfigDir = (process.platform == 'win32' ? process.env.PROGRAMDATA : null) || '/etc';
56
- function typeFromPath(path) {
57
- if (path.startsWith(systemConfigDir + sep))
58
- return 'system';
59
- if (path.startsWith(xdgConfigDir + sep))
60
- return 'user';
61
- return 'local';
62
- }
63
- /** Resolve a `ManagerOptions` path, which may omit the `.json` extension */
64
- function withExtension(path) {
65
- return path.endsWith('.json') ? path : path + '.json';
66
- }
67
- /**
68
- * Remove `.default()` and `.prefault()` from a schema and everything nested within it.
69
- */
70
- function stripDefaults(schema) {
71
- const def = schema._zod.def;
72
- switch (def.type) {
73
- case 'default':
74
- case 'prefault':
75
- return stripDefaults(def.innerType);
76
- case 'object':
77
- return z.core.clone(schema, {
78
- ...def,
79
- shape: Object.fromEntries(Object.entries(def.shape).map(([key, member]) => [key, stripDefaults(member)])),
80
- catchall: def.catchall && stripDefaults(def.catchall),
81
- });
82
- case 'array':
83
- return z.core.clone(schema, { ...def, element: stripDefaults(def.element) });
84
- case 'record':
85
- case 'map':
86
- case 'set':
87
- return z.core.clone(schema, { ...def, valueType: stripDefaults(def.valueType) });
88
- case 'union':
89
- return z.core.clone(schema, { ...def, options: def.options.map(stripDefaults) });
90
- case 'tuple':
91
- return z.core.clone(schema, {
92
- ...def,
93
- items: def.items.map(stripDefaults),
94
- rest: def.rest && stripDefaults(def.rest),
95
- });
96
- case 'optional':
97
- case 'nullable':
98
- case 'readonly':
99
- case 'nonoptional':
100
- case 'catch':
101
- return z.core.clone(schema, { ...def, innerType: stripDefaults(def.innerType) });
102
- default:
103
- return schema;
104
- }
105
- }
106
- /**
107
- * Compute a schema's default value from `.default()` on it and on anything nested within it.
108
- * Members without a default are left out, so the result is not necessarily a complete config.
109
- */
110
- function defaultsOf(schema) {
111
- const def = schema._zod.def;
112
- switch (def.type) {
113
- case 'default':
114
- case 'prefault':
115
- case 'catch':
116
- try {
117
- return schema.parse(undefined);
118
- }
119
- catch {
120
- return defaultsOf(def.innerType);
121
- }
122
- case 'object': {
123
- const value = {};
124
- for (const [key, member] of Object.entries(def.shape)) {
125
- const inner = defaultsOf(member);
126
- if (inner !== undefined)
127
- value[key] = inner;
128
- }
129
- return Object.keys(value).length ? value : undefined;
130
- }
131
- default:
132
- return def.innerType ? defaultsOf(def.innerType) : undefined;
133
- }
134
- }
135
- /**
136
- * Manage for configuration files
137
- */
138
- let Manager = (() => {
139
- let _classSuper = EventEmitter;
140
- let _instanceExtraInitializers = [];
141
- let _get_defaultPaths_decorators;
142
- return class Manager extends _classSuper {
143
- static {
144
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
145
- _get_defaultPaths_decorators = [memoize];
146
- __esDecorate(this, null, _get_defaultPaths_decorators, { kind: "getter", name: "defaultPaths", static: false, private: false, access: { has: obj => "defaultPaths" in obj, get: obj => obj.defaultPaths }, metadata: _metadata }, null, _instanceExtraInitializers);
147
- if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
148
- }
149
- options = __runInitializers(this, _instanceExtraInitializers);
150
- schema;
151
- fileSchema;
152
- files = new Map();
153
- data;
154
- constructor(shape, options = {}) {
155
- super({ captureRejections: true });
156
- this.options = options;
157
- this.schema = z.object(shape);
158
- this.fileSchema = z.deepPartial(stripDefaults(z.object(options.enableIncludes ? { ...shape, include } : shape)));
159
- this.data = this.defaults;
160
- }
161
- /**
162
- * A fresh config with only the schema's defaults applied.
163
- */
164
- get defaults() {
165
- return structuredClone(defaultsOf(this.schema) ?? {});
166
- }
167
- get(key) {
168
- return getByString(this.data, key);
169
- }
170
- set(key, value) {
171
- setByString(this.data, key, value);
172
- }
173
- /**
174
- * Deeply merge `config` into the current config. Arrays are replaced rather than combined.
175
- */
176
- merge(config) {
177
- deepAssign(this.data, structuredClone(config), { replaceArrays: true });
178
- this.emit('change');
179
- }
180
- /** Replace the current config with `config`, applying defaults for anything missing. */
181
- replace(config) {
182
- for (const key of Object.keys(this.data))
183
- delete this.data[key];
184
- Object.assign(this.data, this.schema.parse(config));
185
- this.emit('change');
186
- }
187
- loadFile(path, options) {
188
- if (this.files.has(path))
189
- return;
190
- let json;
191
- try {
192
- json = JSON.parse(readFileSync(path, 'utf8'));
193
- }
194
- catch (e) {
195
- if (!options.create || e.code != 'ENOENT') {
196
- if (!options.optional)
197
- throw e;
198
- if (!this.emit('load_error', path, 'read', e)) {
199
- debug(`Skipping config at ${path} (${e.message})`);
200
- }
201
- return;
202
- }
203
- try {
204
- mkdirSync(dirname(path), { recursive: true });
205
- writeJSON(path, {});
206
- }
207
- catch (e) {
208
- if (!options.optional)
209
- throw e;
210
- if (!this.emit('load_error', path, 'create', e)) {
211
- warn(`Failed to create config at ${path} (${e.message})`);
212
- }
213
- return;
214
- }
215
- debug('Created config: ' + path);
216
- json = {};
217
- }
218
- let file;
219
- try {
220
- file = this.fileSchema.parse(json);
221
- }
222
- catch (e) {
223
- if (!options.loose)
224
- throw e;
225
- if (!this.emit('load_error', path, 'parse', e)) {
226
- debug(`Loading invalid config from ${path} (${e.message})`);
227
- }
228
- file = json;
229
- }
230
- this.files.set(path, {
231
- auto: false,
232
- wasIncluded: false,
233
- type: typeFromPath(path),
234
- ...(options[kInit] || {}),
235
- data: file,
236
- path,
237
- });
238
- this.merge(file);
239
- debug('Loaded config: ' + path);
240
- if (canInclude(this.options, file))
241
- for (const include of file.include ?? []) {
242
- this.loadFile(resolve(dirname(path), include), {
243
- ...options,
244
- optional: true,
245
- create: false,
246
- [kInit]: { ...options[kInit], wasIncluded: true },
247
- });
248
- }
249
- this.emit('load', path, file);
250
- }
251
- /** Get the entry for `path`, adding one for a file that has not been loaded */
252
- fileAt(path) {
253
- const existing = this.files.get(path);
254
- if (existing)
255
- return existing;
256
- const file = {
257
- path,
258
- data: {},
259
- type: typeFromPath(path),
260
- auto: false,
261
- wasIncluded: false,
262
- };
263
- this.files.set(path, file);
264
- return file;
265
- }
266
- writeFile(file) {
267
- mkdirSync(dirname(file.path), { recursive: true });
268
- writeJSON(file.path, file.data);
269
- debug('Wrote config: ' + file.path);
270
- }
271
- /**
272
- * Replace the contents of the config file at `path` and write it,
273
- * then rebuild the current config from the defaults and all loaded files.
274
- */
275
- replaceFile(path, config) {
276
- const file = this.fileAt(path);
277
- file.data = this.fileSchema.parse(config);
278
- this.writeFile(file);
279
- this.replace(this.defaults);
280
- for (const file of this.files.values())
281
- this.merge(file.data);
282
- }
283
- /**
284
- * Merge `config` into the current config and into the config file at `path`, then write it.
285
- */
286
- updateFile(path, config) {
287
- const file = this.fileAt(path);
288
- deepAssign(file.data, this.fileSchema.parse(config), { replaceArrays: true });
289
- this.merge(config);
290
- this.writeFile(file);
291
- }
292
- /**
293
- * The files `loadDefaults` will try to load, ordered from most global to most local.
294
- * @internal
295
- */
296
- get defaultPaths() {
297
- const paths = [];
298
- const { system, xdg } = this.options;
299
- if (system)
300
- paths.push({ type: 'system', path: join(systemConfigDir, withExtension(system)) });
301
- if (xdg)
302
- paths.push({ type: 'user', path: join(xdgConfigDir, withExtension(xdg)) });
303
- return paths;
304
- }
305
- /**
306
- * Load the files from `defaultPaths`. Missing files are skipped unless `options` says otherwise.
307
- */
308
- loadDefaults(options) {
309
- for (const { path, type } of this.defaultPaths) {
310
- this.loadFile(path, { optional: true, ...options, [kInit]: { type, auto: true } });
311
- }
312
- }
313
- /**
314
- * The file a change of the given type is written to:
315
- * the most recently loaded file of that type, or the path `loadDefaults` would use for it.
316
- */
317
- findPath(type) {
318
- const loaded = this.files
319
- .values()
320
- .filter(file => (!type || file.type == type) && !file.wasIncluded)
321
- .map(file => file.path)
322
- .toArray();
323
- if (loaded.length)
324
- return loaded.at(-1);
325
- // Without a type, fall back to the most local default path
326
- const fallback = type ? this.defaultPaths.find(entry => entry.type == type) : this.defaultPaths.at(-1);
327
- if (!fallback)
328
- throw new Error(`No ${type ?? 'writable'} configuration file to write to`);
329
- return fallback.path;
330
- }
331
- /**
332
- * Merge `config` into the current config and save it.
333
- */
334
- update(config, type) {
335
- this.updateFile(this.findPath(type), config);
336
- }
337
- reloadFiles(options) {
338
- const files = this.files
339
- .values()
340
- .filter(file => !file.wasIncluded)
341
- .toArray();
342
- this.files.clear();
343
- this.replace(this.defaults);
344
- for (const file of files) {
345
- this.loadFile(file.path, { ...options, [kInit]: { type: file.type, auto: file.auto } });
346
- }
347
- info('Reloaded configuration files');
348
- }
349
- };
350
- })();
351
- export { Manager };
@@ -1,7 +0,0 @@
1
- import type * as z from 'zod';
2
- export * from '../core.js';
3
- /**
4
- * @param defaultValue Returned when the file can't be loaded. If omitted, loading errors will be thrown.
5
- */
6
- export declare function readJSON<S extends z.ZodType>(path: string, schema: S): z.infer<S>;
7
- export declare function writeJSON(path: string, data: any): void;
package/dist/node/core.js DELETED
@@ -1,198 +0,0 @@
1
- // SPDX-License-Identifier: LGPL-3.0-or-later
2
- /* eslint-disable @typescript-eslint/only-throw-error */
3
- var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
4
- if (value !== null && value !== void 0) {
5
- if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
6
- var dispose, inner;
7
- if (async) {
8
- if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
9
- dispose = value[Symbol.asyncDispose];
10
- }
11
- if (dispose === void 0) {
12
- if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
13
- dispose = value[Symbol.dispose];
14
- if (async) inner = dispose;
15
- }
16
- if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
17
- if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
18
- env.stack.push({ value: value, dispose: dispose, async: async });
19
- }
20
- else if (async) {
21
- env.stack.push({ async: true });
22
- }
23
- return value;
24
- };
25
- var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
26
- return function (env) {
27
- function fail(e) {
28
- env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
29
- env.hasError = true;
30
- }
31
- var r, s = 0;
32
- function next() {
33
- while (r = env.stack.pop()) {
34
- try {
35
- if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
36
- if (r.dispose) {
37
- var result = r.dispose.call(r.value);
38
- if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
39
- }
40
- else s |= 1;
41
- }
42
- catch (e) {
43
- fail(e);
44
- }
45
- }
46
- if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
47
- if (env.hasError) throw env.error;
48
- }
49
- return next();
50
- };
51
- })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
52
- var e = new Error(message);
53
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
54
- });
55
- import { readFileSync, writeFileSync } from 'node:fs';
56
- import { styleText } from 'node:util';
57
- import * as io from '../core.js';
58
- export * from '../core.js';
59
- let _currentOperation = null, _progress = null;
60
- function clearLine() {
61
- if (!process.stdout.isTTY)
62
- return;
63
- process.stdout.clearLine(0);
64
- process.stdout.cursorTo(0);
65
- }
66
- function handleProgress() {
67
- if (!_currentOperation)
68
- return { [Symbol.dispose]() { } };
69
- clearLine();
70
- return {
71
- [Symbol.dispose]() {
72
- process.stdout.write(_currentOperation + '... ');
73
- if (_progress)
74
- io.progress(..._progress);
75
- },
76
- };
77
- }
78
- io.useProgress({
79
- start(message) {
80
- _currentOperation = message;
81
- process.stdout.write(message + '... ');
82
- },
83
- progress(value, max, message, text) {
84
- _progress = [value, max];
85
- clearLine();
86
- process.stdout.write(`${_currentOperation}... ${text || value.toString().padStart(max.toString().length) + '/' + max} ${message && value < max ? `(${message})` : ''}`);
87
- if (value >= max) {
88
- _currentOperation = null;
89
- _progress = null;
90
- console.log();
91
- }
92
- },
93
- done(noPrint) {
94
- _currentOperation = null;
95
- _progress = null;
96
- if (!noPrint)
97
- console.log('done.');
98
- },
99
- });
100
- function* maybeStyle(style, parts) {
101
- for (const part of parts) {
102
- if (typeof part != 'string')
103
- yield part;
104
- else if (part.startsWith('\x1b'))
105
- yield part;
106
- else
107
- yield styleText(style, part);
108
- }
109
- }
110
- io.useOutput({
111
- error(...message) {
112
- const env_1 = { stack: [], error: void 0, hasError: false };
113
- try {
114
- const _ = __addDisposableResource(env_1, handleProgress(), false);
115
- console.error(...maybeStyle('red', message));
116
- }
117
- catch (e_1) {
118
- env_1.error = e_1;
119
- env_1.hasError = true;
120
- }
121
- finally {
122
- __disposeResources(env_1);
123
- }
124
- },
125
- warn(...message) {
126
- const env_2 = { stack: [], error: void 0, hasError: false };
127
- try {
128
- const _ = __addDisposableResource(env_2, handleProgress(), false);
129
- console.warn(...maybeStyle('yellow', message));
130
- }
131
- catch (e_2) {
132
- env_2.error = e_2;
133
- env_2.hasError = true;
134
- }
135
- finally {
136
- __disposeResources(env_2);
137
- }
138
- },
139
- info(...message) {
140
- const env_3 = { stack: [], error: void 0, hasError: false };
141
- try {
142
- const _ = __addDisposableResource(env_3, handleProgress(), false);
143
- console.info(...maybeStyle('blue', message));
144
- }
145
- catch (e_3) {
146
- env_3.error = e_3;
147
- env_3.hasError = true;
148
- }
149
- finally {
150
- __disposeResources(env_3);
151
- }
152
- },
153
- log(...message) {
154
- const env_4 = { stack: [], error: void 0, hasError: false };
155
- try {
156
- const _ = __addDisposableResource(env_4, handleProgress(), false);
157
- console.log(...message);
158
- }
159
- catch (e_4) {
160
- env_4.error = e_4;
161
- env_4.hasError = true;
162
- }
163
- finally {
164
- __disposeResources(env_4);
165
- }
166
- },
167
- debug(...message) {
168
- const env_5 = { stack: [], error: void 0, hasError: false };
169
- try {
170
- if (!io._debugOutput)
171
- return;
172
- const _ = __addDisposableResource(env_5, handleProgress(), false);
173
- console.debug(...maybeStyle('gray', message));
174
- }
175
- catch (e_5) {
176
- env_5.error = e_5;
177
- env_5.hasError = true;
178
- }
179
- finally {
180
- __disposeResources(env_5);
181
- }
182
- },
183
- });
184
- /**
185
- * @param defaultValue Returned when the file can't be loaded. If omitted, loading errors will be thrown.
186
- */
187
- export function readJSON(path, schema) {
188
- try {
189
- const data = JSON.parse(readFileSync(path, 'utf-8'));
190
- return schema.parse(data);
191
- }
192
- catch (e) {
193
- throw io.errorText(e);
194
- }
195
- }
196
- export function writeJSON(path, data) {
197
- writeFileSync(path, JSON.stringify(data, null, '\t'), 'utf-8');
198
- }
@@ -1,7 +0,0 @@
1
- export * from '../table.js';
2
- export * from '../tracking.js';
3
- export * from './core.js';
4
- export * as jobs from './jobs.js';
5
- export * from './process.js';
6
- export * from './rl.js';
7
- export * as config from './config.js';
@@ -1,17 +0,0 @@
1
- // SPDX-License-Identifier: LGPL-3.0-or-later
2
- import { stripVTControlCharacters } from 'node:util';
3
- import { setANSIStripper, setTableTargetWidth } from '../table.js';
4
- setANSIStripper(stripVTControlCharacters);
5
- if (process.stdout.isTTY) {
6
- setTableTargetWidth(process.stdout.columns);
7
- process.stdout.on('resize', () => {
8
- setTableTargetWidth(process.stdout.columns);
9
- });
10
- }
11
- export * from '../table.js';
12
- export * from '../tracking.js';
13
- export * from './core.js';
14
- export * as jobs from './jobs.js';
15
- export * from './process.js';
16
- export * from './rl.js';
17
- export * as config from './config.js';
@@ -1,11 +0,0 @@
1
- import * as jobs from '../jobs.js';
2
- export * from '../jobs.js';
3
- export interface CommandData {
4
- argv: string[];
5
- name?: string;
6
- }
7
- export interface CommandOptions extends jobs.Options {
8
- parseLine?(rawLine: string, isStderr: boolean): string;
9
- successText?: string;
10
- }
11
- export declare function runCommands(options: CommandOptions, commands: CommandData[]): Promise<jobs.Results>;
package/dist/node/jobs.js DELETED
@@ -1,76 +0,0 @@
1
- // SPDX-License-Identifier: LGPL-3.0-or-later
2
- import { spawn } from 'node:child_process';
3
- import * as jobs from '../jobs.js';
4
- import { styleText } from 'node:util';
5
- import { errorText } from '../core.js';
6
- export * from '../jobs.js';
7
- const isTTY = !!process.stdout.isTTY && !!process.stderr.isTTY;
8
- jobs.useClear(lastDrawLineCount => {
9
- process.stdout.moveCursor(0, -lastDrawLineCount);
10
- process.stdout.clearScreenDown();
11
- });
12
- jobs.useDraw((lines) => {
13
- for (const line of lines)
14
- process.stdout.write(line + '\n');
15
- });
16
- jobs.useResultFormat(result => result.status == 'failed'
17
- ? styleText('red', result.text?.toString() ?? 'failed')
18
- : (result.text?.toString() ?? styleText('green', 'done')));
19
- if (!isTTY)
20
- jobs._disableInPlaceUpdates();
21
- async function runCommand(data, progress) {
22
- const [command, ...args] = data.argv;
23
- const options = this;
24
- const child = spawn(command, args, {
25
- stdio: [
26
- 'ignore',
27
- // stdout: ignore in TTY mode (we synthesize our own status), inherit otherwise
28
- isTTY ? 'pipe' : 'inherit',
29
- // stderr: parse in TTY mode, inherit otherwise
30
- isTTY ? 'pipe' : 'inherit',
31
- ],
32
- });
33
- let failText = null, failSetFromThrow = false;
34
- function handleStream(stream, isErr) {
35
- if (!isTTY || !stream)
36
- return;
37
- let buf = '';
38
- stream.on('data', (chunk) => {
39
- buf += chunk.toString('utf8');
40
- const lines = buf.split(/[\r\n]+/);
41
- buf = lines.pop() ?? '';
42
- for (const rawLine of lines) {
43
- let line = rawLine.trim();
44
- if (!line)
45
- continue;
46
- try {
47
- line = options.parseLine?.(line, isErr) ?? line;
48
- if (isErr && !failSetFromThrow)
49
- failText ||= line;
50
- }
51
- catch (e) {
52
- line = errorText(e);
53
- failText = line;
54
- failSetFromThrow = true;
55
- }
56
- progress(line);
57
- }
58
- });
59
- }
60
- handleStream(child.stdout, false);
61
- handleStream(child.stderr, true);
62
- const { promise: close, resolve } = Promise.withResolvers();
63
- child.on('close', resolve);
64
- const code = await close;
65
- return code === 0
66
- ? { status: 'succeeded', text: options.successText ?? styleText('green', 'done') }
67
- : { status: 'failed', text: failText ?? (code ? `failed (${code})` : 'failed') };
68
- }
69
- export async function runCommands(options, commands) {
70
- return await jobs.runWithData({
71
- ...options,
72
- jobStartText: styleText('cyan', 'starting...'),
73
- run: runCommand.bind(options),
74
- name: cmd => cmd.name,
75
- }, commands);
76
- }