@listr2/manager
Version:
Extension for Listr2 to easily create sane defaults for task lists.
2,426 lines • 84.3 kB
JavaScript
import { __commonJS, __toESM } from "./chunk-Ye9CTm_R.js";
import * as tty from "tty";
import { format } from "util";
import { EOL } from "os";
import { StringDecoder } from "string_decoder";
import { Writable } from "stream";
import { randomUUID } from "crypto";
//#region ../../node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.js
var require_eventemitter3 = __commonJS({ "../../node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.js"(exports, module) {
var has = Object.prototype.hasOwnProperty, prefix = "~";
/**
* Constructor to create a storage for our `EE` objects.
* An `Events` instance is a plain object whose properties are event names.
*
* @constructor
* @private
*/
function Events() {}
if (Object.create) {
Events.prototype = Object.create(null);
if (!new Events().__proto__) prefix = false;
}
/**
* Representation of a single event listener.
*
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
* @constructor
* @private
*/
function EE(fn, context, once) {
this.fn = fn;
this.context = context;
this.once = once || false;
}
/**
* Add a listener for a given event.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} once Specify if the listener is a one-time listener.
* @returns {EventEmitter}
* @private
*/
function addListener(emitter, event, fn, context, once) {
if (typeof fn !== "function") throw new TypeError("The listener must be a function");
var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event;
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
else emitter._events[evt] = [emitter._events[evt], listener];
return emitter;
}
/**
* Clear event by name.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} evt The Event name.
* @private
*/
function clearEvent(emitter, evt) {
if (--emitter._eventsCount === 0) emitter._events = new Events();
else delete emitter._events[evt];
}
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*
* @constructor
* @public
*/
function EventEmitter$1() {
this._events = new Events();
this._eventsCount = 0;
}
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*
* @returns {Array}
* @public
*/
EventEmitter$1.prototype.eventNames = function eventNames() {
var names = [], events, name;
if (this._eventsCount === 0) return names;
for (name in events = this._events) if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
if (Object.getOwnPropertySymbols) return names.concat(Object.getOwnPropertySymbols(events));
return names;
};
/**
* Return the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Array} The registered listeners.
* @public
*/
EventEmitter$1.prototype.listeners = function listeners(event) {
var evt = prefix ? prefix + event : event, handlers = this._events[evt];
if (!handlers) return [];
if (handlers.fn) return [handlers.fn];
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) ee[i] = handlers[i].fn;
return ee;
};
/**
* Return the number of listeners listening to a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Number} The number of listeners.
* @public
*/
EventEmitter$1.prototype.listenerCount = function listenerCount(event) {
var evt = prefix ? prefix + event : event, listeners = this._events[evt];
if (!listeners) return 0;
if (listeners.fn) return 1;
return listeners.length;
};
/**
* Calls each of the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Boolean} `true` if the event had listeners, else `false`.
* @public
*/
EventEmitter$1.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return false;
var listeners = this._events[evt], len = arguments.length, args, i;
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, void 0, true);
switch (len) {
case 1: return listeners.fn.call(listeners.context), true;
case 2: return listeners.fn.call(listeners.context, a1), true;
case 3: return listeners.fn.call(listeners.context, a1, a2), true;
case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len - 1); i < len; i++) args[i - 1] = arguments[i];
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length, j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, void 0, true);
switch (len) {
case 1:
listeners[i].fn.call(listeners[i].context);
break;
case 2:
listeners[i].fn.call(listeners[i].context, a1);
break;
case 3:
listeners[i].fn.call(listeners[i].context, a1, a2);
break;
case 4:
listeners[i].fn.call(listeners[i].context, a1, a2, a3);
break;
default:
if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) args[j - 1] = arguments[j];
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
};
/**
* Add a listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter$1.prototype.on = function on(event, fn, context) {
return addListener(this, event, fn, context, false);
};
/**
* Add a one-time listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter$1.prototype.once = function once(event, fn, context) {
return addListener(this, event, fn, context, true);
};
/**
* Remove the listeners of a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn Only remove the listeners that match this function.
* @param {*} context Only remove the listeners that have this context.
* @param {Boolean} once Only remove one-time listeners.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter$1.prototype.removeListener = function removeListener(event, fn, context, once) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return this;
if (!fn) {
clearEvent(this, evt);
return this;
}
var listeners = this._events[evt];
if (listeners.fn) {
if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) clearEvent(this, evt);
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) events.push(listeners[i]);
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
else clearEvent(this, evt);
}
return this;
};
/**
* Remove all listeners, or those of the specified event.
*
* @param {(String|Symbol)} [event] The event name.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter$1.prototype.removeAllListeners = function removeAllListeners(event) {
var evt;
if (event) {
evt = prefix ? prefix + event : event;
if (this._events[evt]) clearEvent(this, evt);
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
};
EventEmitter$1.prototype.off = EventEmitter$1.prototype.removeListener;
EventEmitter$1.prototype.addListener = EventEmitter$1.prototype.on;
EventEmitter$1.prefixed = prefix;
EventEmitter$1.EventEmitter = EventEmitter$1;
if ("undefined" !== typeof module) module.exports = EventEmitter$1;
} });
//#endregion
//#region ../../node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.mjs
var import_eventemitter3 = __toESM(require_eventemitter3(), 1);
var eventemitter3_default = import_eventemitter3.default;
//#endregion
//#region ../../node_modules/.pnpm/colorette@2.0.20/node_modules/colorette/index.js
const { env = {}, argv = [], platform = "" } = typeof process === "undefined" ? {} : process;
const isDisabled = "NO_COLOR" in env || argv.includes("--no-color");
const isForced = "FORCE_COLOR" in env || argv.includes("--color");
const isWindows = platform === "win32";
const isDumbTerminal = env.TERM === "dumb";
const isCompatibleTerminal = tty && tty.isatty && tty.isatty(1) && env.TERM && !isDumbTerminal;
const isCI = "CI" in env && ("GITHUB_ACTIONS" in env || "GITLAB_CI" in env || "CIRCLECI" in env);
const isColorSupported = !isDisabled && (isForced || isWindows && !isDumbTerminal || isCompatibleTerminal || isCI);
const replaceClose = (index, string, close, replace, head = string.substring(0, index) + replace, tail = string.substring(index + close.length), next = tail.indexOf(close)) => head + (next < 0 ? tail : replaceClose(next, tail, close, replace));
const clearBleed = (index, string, open, close, replace) => index < 0 ? open + string + close : open + replaceClose(index, string, close, replace) + close;
const filterEmpty = (open, close, replace = open, at = open.length + 1) => (string) => string || !(string === "" || string === void 0) ? clearBleed(("" + string).indexOf(close, at), string, open, close, replace) : "";
const init = (open, close, replace) => filterEmpty(`\x1b[${open}m`, `\x1b[${close}m`, replace);
const colors = {
reset: init(0, 0),
bold: init(1, 22, "\x1B[22m\x1B[1m"),
dim: init(2, 22, "\x1B[22m\x1B[2m"),
italic: init(3, 23),
underline: init(4, 24),
inverse: init(7, 27),
hidden: init(8, 28),
strikethrough: init(9, 29),
black: init(30, 39),
red: init(31, 39),
green: init(32, 39),
yellow: init(33, 39),
blue: init(34, 39),
magenta: init(35, 39),
cyan: init(36, 39),
white: init(37, 39),
gray: init(90, 39),
bgBlack: init(40, 49),
bgRed: init(41, 49),
bgGreen: init(42, 49),
bgYellow: init(43, 49),
bgBlue: init(44, 49),
bgMagenta: init(45, 49),
bgCyan: init(46, 49),
bgWhite: init(47, 49),
blackBright: init(90, 39),
redBright: init(91, 39),
greenBright: init(92, 39),
yellowBright: init(93, 39),
blueBright: init(94, 39),
magentaBright: init(95, 39),
cyanBright: init(96, 39),
whiteBright: init(97, 39),
bgBlackBright: init(100, 49),
bgRedBright: init(101, 49),
bgGreenBright: init(102, 49),
bgYellowBright: init(103, 49),
bgBlueBright: init(104, 49),
bgMagentaBright: init(105, 49),
bgCyanBright: init(106, 49),
bgWhiteBright: init(107, 49)
};
const createColors = ({ useColor = isColorSupported } = {}) => useColor ? colors : Object.keys(colors).reduce((colors$1, key) => ({
...colors$1,
[key]: String
}), {});
const { reset, bold, dim, italic, underline, inverse, hidden, strikethrough, black, red, green, yellow, blue, magenta, cyan, white, gray, bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite, blackBright, redBright, greenBright, yellowBright, blueBright, magentaBright, cyanBright, whiteBright, bgBlackBright, bgRedBright, bgGreenBright, bgYellowBright, bgBlueBright, bgMagentaBright, bgCyanBright, bgWhiteBright } = createColors();
//#endregion
//#region ../../node_modules/.pnpm/rfdc@1.4.1/node_modules/rfdc/index.js
var require_rfdc = __commonJS({ "../../node_modules/.pnpm/rfdc@1.4.1/node_modules/rfdc/index.js"(exports, module) {
module.exports = rfdc$1;
function copyBuffer(cur) {
if (cur instanceof Buffer) return Buffer.from(cur);
return new cur.constructor(cur.buffer.slice(), cur.byteOffset, cur.length);
}
function rfdc$1(opts) {
opts = opts || {};
if (opts.circles) return rfdcCircles(opts);
const constructorHandlers = /* @__PURE__ */ new Map();
constructorHandlers.set(Date, (o) => new Date(o));
constructorHandlers.set(Map, (o, fn) => new Map(cloneArray(Array.from(o), fn)));
constructorHandlers.set(Set, (o, fn) => new Set(cloneArray(Array.from(o), fn)));
if (opts.constructorHandlers) for (const handler$1 of opts.constructorHandlers) constructorHandlers.set(handler$1[0], handler$1[1]);
let handler = null;
return opts.proto ? cloneProto : clone$1;
function cloneArray(a, fn) {
const keys = Object.keys(a);
const a2 = new Array(keys.length);
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
const cur = a[k];
if (typeof cur !== "object" || cur === null) a2[k] = cur;
else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) a2[k] = handler(cur, fn);
else if (ArrayBuffer.isView(cur)) a2[k] = copyBuffer(cur);
else a2[k] = fn(cur);
}
return a2;
}
function clone$1(o) {
if (typeof o !== "object" || o === null) return o;
if (Array.isArray(o)) return cloneArray(o, clone$1);
if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) return handler(o, clone$1);
const o2 = {};
for (const k in o) {
if (Object.hasOwnProperty.call(o, k) === false) continue;
const cur = o[k];
if (typeof cur !== "object" || cur === null) o2[k] = cur;
else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) o2[k] = handler(cur, clone$1);
else if (ArrayBuffer.isView(cur)) o2[k] = copyBuffer(cur);
else o2[k] = clone$1(cur);
}
return o2;
}
function cloneProto(o) {
if (typeof o !== "object" || o === null) return o;
if (Array.isArray(o)) return cloneArray(o, cloneProto);
if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) return handler(o, cloneProto);
const o2 = {};
for (const k in o) {
const cur = o[k];
if (typeof cur !== "object" || cur === null) o2[k] = cur;
else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) o2[k] = handler(cur, cloneProto);
else if (ArrayBuffer.isView(cur)) o2[k] = copyBuffer(cur);
else o2[k] = cloneProto(cur);
}
return o2;
}
}
function rfdcCircles(opts) {
const refs = [];
const refsNew = [];
const constructorHandlers = /* @__PURE__ */ new Map();
constructorHandlers.set(Date, (o) => new Date(o));
constructorHandlers.set(Map, (o, fn) => new Map(cloneArray(Array.from(o), fn)));
constructorHandlers.set(Set, (o, fn) => new Set(cloneArray(Array.from(o), fn)));
if (opts.constructorHandlers) for (const handler$1 of opts.constructorHandlers) constructorHandlers.set(handler$1[0], handler$1[1]);
let handler = null;
return opts.proto ? cloneProto : clone$1;
function cloneArray(a, fn) {
const keys = Object.keys(a);
const a2 = new Array(keys.length);
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
const cur = a[k];
if (typeof cur !== "object" || cur === null) a2[k] = cur;
else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) a2[k] = handler(cur, fn);
else if (ArrayBuffer.isView(cur)) a2[k] = copyBuffer(cur);
else {
const index = refs.indexOf(cur);
if (index !== -1) a2[k] = refsNew[index];
else a2[k] = fn(cur);
}
}
return a2;
}
function clone$1(o) {
if (typeof o !== "object" || o === null) return o;
if (Array.isArray(o)) return cloneArray(o, clone$1);
if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) return handler(o, clone$1);
const o2 = {};
refs.push(o);
refsNew.push(o2);
for (const k in o) {
if (Object.hasOwnProperty.call(o, k) === false) continue;
const cur = o[k];
if (typeof cur !== "object" || cur === null) o2[k] = cur;
else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) o2[k] = handler(cur, clone$1);
else if (ArrayBuffer.isView(cur)) o2[k] = copyBuffer(cur);
else {
const i = refs.indexOf(cur);
if (i !== -1) o2[k] = refsNew[i];
else o2[k] = clone$1(cur);
}
}
refs.pop();
refsNew.pop();
return o2;
}
function cloneProto(o) {
if (typeof o !== "object" || o === null) return o;
if (Array.isArray(o)) return cloneArray(o, cloneProto);
if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) return handler(o, cloneProto);
const o2 = {};
refs.push(o);
refsNew.push(o2);
for (const k in o) {
const cur = o[k];
if (typeof cur !== "object" || cur === null) o2[k] = cur;
else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) o2[k] = handler(cur, cloneProto);
else if (ArrayBuffer.isView(cur)) o2[k] = copyBuffer(cur);
else {
const i = refs.indexOf(cur);
if (i !== -1) o2[k] = refsNew[i];
else o2[k] = cloneProto(cur);
}
}
refs.pop();
refsNew.pop();
return o2;
}
}
} });
//#endregion
//#region ../listr2/dist/index.js
var import_rfdc = __toESM(require_rfdc(), 1);
/**
* Indicates an UNICODE characters is coming up.
*/
const ANSI_ESCAPE = "\x1B[";
/**
* Generic ANSI escape characters for terminal based operations.
*/
const ANSI_ESCAPE_CODES = {
CURSOR_HIDE: ANSI_ESCAPE + "?25l",
CURSOR_SHOW: ANSI_ESCAPE + "?25h"
};
/**
* Environment variables for Listr.
*/
let ListrEnvironmentVariables = /* @__PURE__ */ function(ListrEnvironmentVariables$1) {
ListrEnvironmentVariables$1["FORCE_UNICODE"] = "LISTR_FORCE_UNICODE";
ListrEnvironmentVariables$1["FORCE_TTY"] = "LISTR_FORCE_TTY";
ListrEnvironmentVariables$1["DISABLE_COLOR"] = "NO_COLOR";
ListrEnvironmentVariables$1["FORCE_COLOR"] = "FORCE_COLOR";
return ListrEnvironmentVariables$1;
}({});
/**
* The actual error type that is collected and to help identify where the error is triggered from.
*/
let ListrErrorTypes = /* @__PURE__ */ function(ListrErrorTypes$1) {
/** Task has failed and will try to retry. */
ListrErrorTypes$1["WILL_RETRY"] = "WILL_RETRY";
/** Task has failed and will try to rollback. */
ListrErrorTypes$1["WILL_ROLLBACK"] = "WILL_ROLLBACK";
/** Task has failed, ran the rollback action but the rollback action itself has failed. */
ListrErrorTypes$1["HAS_FAILED_TO_ROLLBACK"] = "HAS_FAILED_TO_ROLLBACK";
/** Task has failed. */
ListrErrorTypes$1["HAS_FAILED"] = "HAS_FAILED";
/** Task has failed, but exitOnError is set to false, so will ignore this error. */
ListrErrorTypes$1["HAS_FAILED_WITHOUT_ERROR"] = "HAS_FAILED_WITHOUT_ERROR";
return ListrErrorTypes$1;
}({});
/**
* Events that are triggered by Listr.
*
* These are stateful and singleton events by being attached to the main Listr class and propagating to the subtasks.
*
* @see {@link https://listr2.kilic.dev/listr/events.html}
*/
let ListrEventType = /* @__PURE__ */ function(ListrEventType$1) {
/** Indicates that underlying renderer should refresh the current render. */
ListrEventType$1["SHOULD_REFRESH_RENDER"] = "SHOUD_REFRESH_RENDER";
return ListrEventType$1;
}({});
let ListrRendererSelection = /* @__PURE__ */ function(ListrRendererSelection$1) {
ListrRendererSelection$1["PRIMARY"] = "PRIMARY";
ListrRendererSelection$1["SECONDARY"] = "SECONDARY";
ListrRendererSelection$1["SILENT"] = "SILENT";
return ListrRendererSelection$1;
}({});
/**
* Internal events that are fired from the Task.
*
* @see {@link https://listr2.kilic.dev/task/events.html}
*/
let ListrTaskEventType = /* @__PURE__ */ function(ListrTaskEventType$1) {
/** Title has changed for the current Task. */
ListrTaskEventType$1["TITLE"] = "TITLE";
/**
* State has changed for the current Task.
*
* @see {@link module:listr2.ListrTaskState}
*/
ListrTaskEventType$1["STATE"] = "STATE";
/** The current Task has been marked as enabled. */
ListrTaskEventType$1["ENABLED"] = "ENABLED";
/** The current Task is currently processing subtasks. */
ListrTaskEventType$1["SUBTASK"] = "SUBTASK";
/** The current Task is now processing a prompt. */
ListrTaskEventType$1["PROMPT"] = "PROMPT";
/** The current Task is now dumping output. */
ListrTaskEventType$1["OUTPUT"] = "OUTPUT";
/**
* The current Task is now dumping a message.
*
* @see {module:Listr2.ListrTaskMessage}
*/
ListrTaskEventType$1["MESSAGE"] = "MESSAGE";
/** The current Task is closed and no further action in expected. */
ListrTaskEventType$1["CLOSED"] = "CLOSED";
return ListrTaskEventType$1;
}({});
/**
* Tasks can be in various states during the execution.
*
* Whenever a state change occurs, the task will emit a {@link module:listr2.ListrTaskEventType.STATE} with the appropriate state.
*/
let ListrTaskState = /* @__PURE__ */ function(ListrTaskState$1) {
/** Task has not started yet, waiting for pick-up. */
ListrTaskState$1["WAITING"] = "WAITING";
/** Task has started. */
ListrTaskState$1["STARTED"] = "STARTED";
/** Task has been completed. */
ListrTaskState$1["COMPLETED"] = "COMPLETED";
/** Task has failed. */
ListrTaskState$1["FAILED"] = "FAILED";
/** Task has been skipped. */
ListrTaskState$1["SKIPPED"] = "SKIPPED";
/** Task is currently trying to rollback. */
ListrTaskState$1["ROLLING_BACK"] = "ROLLING_BACK";
/** Task has rolledback successfully after failing. */
ListrTaskState$1["ROLLED_BACK"] = "ROLLED_BACK";
/** Task is currently retrying. */
ListrTaskState$1["RETRY"] = "RETRY";
/** Task is currently paused. */
ListrTaskState$1["PAUSED"] = "PAUSED";
/** Task is currently trying to process a prompt. */
ListrTaskState$1["PROMPT"] = "PROMPT";
/** Task has successfully processed the prompt. */
ListrTaskState$1["PROMPT_COMPLETED"] = "PROMPT_COMPLETED";
/** Task has failed to process the prompt. */
ListrTaskState$1["PROMPT_FAILED"] = "PROMPT_FAILED";
return ListrTaskState$1;
}({});
var EventManager = class {
emitter = new eventemitter3_default();
emit(dispatch, args) {
this.emitter.emit(dispatch, args);
}
on(dispatch, handler) {
this.emitter.addListener(dispatch, handler);
}
once(dispatch, handler) {
this.emitter.once(dispatch, handler);
}
off(dispatch, handler) {
this.emitter.off(dispatch, handler);
}
complete() {
this.emitter.removeAllListeners();
}
};
/**
* Tests to see if the object is an RxJS {@link Observable}
* @param obj the object to test
*/
function isObservable(obj) {
return !!obj && typeof obj === "object" && typeof obj.subscribe === "function";
}
/**
* Tests to see if the object is an Readable or NodeJS.ReadableStream {@link Readable, NodeJS.ReadableStream}
* @param obj the object to test
*/
function isReadable(obj) {
return !!obj && typeof obj === "object" && obj.readable === true && typeof obj.read === "function" && typeof obj.on === "function";
}
function isUnicodeSupported() {
/* istanbul ignore next */
return !!process.env[ListrEnvironmentVariables.FORCE_UNICODE] || process.platform !== "win32" || !!process.env.CI || !!process.env.WT_SESSION || process.env.TERM_PROGRAM === "vscode" || process.env.TERM === "xterm-256color" || process.env.TERM === "alacritty";
}
const CLEAR_LINE_REGEX = "(?:\\u001b|\\u009b)\\[[\\=><~/#&.:=?%@~_-]*[0-9]*[\\a-ln-tqyz=><~/#&.:=?%@~_-]+";
const BELL_REGEX = /\u0007/;
function cleanseAnsi(chunk) {
return String(chunk).replace(new RegExp(CLEAR_LINE_REGEX, "gmi"), "").replace(new RegExp(BELL_REGEX, "gmi"), "").trim();
}
/**
* Creates color palette through underlying dependency of `colorette`.
*
* @see {@link https://www.npmjs.com/package/colorette}
*/
const color = createColors();
function indent(string, count) {
return string.replace(/^(?!\s*$)/gm, " ".repeat(count));
}
const FIGURES_MAIN = {
warning: "⚠",
cross: "✖",
arrowDown: "↓",
tick: "✔",
arrowRight: "→",
pointer: "❯",
checkboxOn: "☒",
arrowLeft: "←",
squareSmallFilled: "◼",
pointerSmall: "›"
};
const FIGURES_FALLBACK = {
...FIGURES_MAIN,
warning: "‼",
cross: "×",
tick: "√",
pointer: ">",
checkboxOn: "[×]",
squareSmallFilled: "■"
};
const figures = isUnicodeSupported() ? FIGURES_MAIN : FIGURES_FALLBACK;
function splat(message, ...splat$1) {
return format(String(message), ...splat$1);
}
/** Default ListrLogLevels for the logger */
let ListrLogLevels = /* @__PURE__ */ function(ListrLogLevels$1) {
ListrLogLevels$1["STARTED"] = "STARTED";
ListrLogLevels$1["COMPLETED"] = "COMPLETED";
ListrLogLevels$1["FAILED"] = "FAILED";
ListrLogLevels$1["SKIPPED"] = "SKIPPED";
ListrLogLevels$1["OUTPUT"] = "OUTPUT";
ListrLogLevels$1["TITLE"] = "TITLE";
ListrLogLevels$1["ROLLBACK"] = "ROLLBACK";
ListrLogLevels$1["RETRY"] = "RETRY";
ListrLogLevels$1["PROMPT"] = "PROMPT";
ListrLogLevels$1["PAUSED"] = "PAUSED";
return ListrLogLevels$1;
}({});
const LISTR_LOGGER_STYLE = {
icon: {
[ListrLogLevels.STARTED]: figures.pointer,
[ListrLogLevels.FAILED]: figures.cross,
[ListrLogLevels.SKIPPED]: figures.arrowDown,
[ListrLogLevels.COMPLETED]: figures.tick,
[ListrLogLevels.OUTPUT]: figures.pointerSmall,
[ListrLogLevels.TITLE]: figures.arrowRight,
[ListrLogLevels.RETRY]: figures.warning,
[ListrLogLevels.ROLLBACK]: figures.arrowLeft,
[ListrLogLevels.PAUSED]: figures.squareSmallFilled
},
color: {
[ListrLogLevels.STARTED]: color.yellow,
[ListrLogLevels.FAILED]: color.red,
[ListrLogLevels.SKIPPED]: color.yellow,
[ListrLogLevels.COMPLETED]: color.green,
[ListrLogLevels.RETRY]: color.yellowBright,
[ListrLogLevels.ROLLBACK]: color.redBright,
[ListrLogLevels.PAUSED]: color.yellowBright
}
};
const LISTR_LOGGER_STDERR_LEVELS = [
ListrLogLevels.RETRY,
ListrLogLevels.ROLLBACK,
ListrLogLevels.FAILED
];
/**
* Creates a new Listr2 logger.
*
* This logger is used throughout the renderers for consistency.
*
* @see {@link https://listr2.kilic.dev/renderer/logger.html}
*/
var ListrLogger = class {
process;
constructor(options) {
this.options = options;
this.options = {
useIcons: true,
toStderr: [],
...options ?? {}
};
this.options.fields ??= {};
this.options.fields.prefix ??= [];
this.options.fields.suffix ??= [];
this.process = this.options.processOutput ?? new ProcessOutput();
}
log(level, message, options) {
const output = this.format(level, message, options);
if (this.options.toStderr.includes(level)) {
this.process.toStderr(output);
return;
}
this.process.toStdout(output);
}
toStdout(message, options, eol = true) {
this.process.toStdout(this.format(null, message, options), eol);
}
toStderr(message, options, eol = true) {
this.process.toStderr(this.format(null, message, options), eol);
}
wrap(message, options) {
if (!message) return message;
return this.applyFormat(`[${message}]`, options);
}
splat(...args) {
const message = args.shift() ?? "";
return args.length === 0 ? message : splat(message, args);
}
suffix(message, ...suffixes) {
suffixes.filter(Boolean).forEach((suffix) => {
message += this.spacing(message);
if (typeof suffix === "string") message += this.wrap(suffix);
else if (typeof suffix === "object") {
suffix.args ??= [];
if (typeof suffix.condition === "function" ? !suffix.condition(...suffix.args) : !(suffix.condition ?? true)) return message;
message += this.wrap(typeof suffix.field === "function" ? suffix.field(...suffix.args) : suffix.field, { format: suffix?.format(...suffix.args) });
}
});
return message;
}
prefix(message, ...prefixes) {
prefixes.filter(Boolean).forEach((prefix$1) => {
message = this.spacing(message) + message;
if (typeof prefix$1 === "string") message = this.wrap(prefix$1) + message;
else if (typeof prefix$1 === "object") {
prefix$1.args ??= [];
if (typeof prefix$1.condition === "function" ? !prefix$1.condition(...prefix$1.args) : !(prefix$1.condition ?? true)) return message;
message = this.wrap(typeof prefix$1.field === "function" ? prefix$1.field(...prefix$1.args) : prefix$1.field, { format: prefix$1?.format() }) + message;
}
});
return message;
}
fields(message, options) {
if (this.options?.fields?.prefix) message = this.prefix(message, ...this.options.fields.prefix);
if (options?.prefix) message = this.prefix(message, ...options.prefix);
if (options?.suffix) message = this.suffix(message, ...options.suffix);
if (this.options?.fields?.suffix) message = this.suffix(message, ...this.options.fields.suffix);
return message;
}
icon(level, icon) {
if (!level) return null;
if (!icon) {
const i = this.options.icon?.[level];
icon = typeof i === "function" ? i() : i;
}
const coloring = this.options.color?.[level];
if (icon && coloring) icon = coloring(icon);
return icon;
}
format(level, message, options) {
if (!Array.isArray(message)) message = [message];
message = this.splat(message.shift(), ...message).toString().split(EOL).filter((m) => !m || m.trim() !== "").map((m) => {
return this.style(level, this.fields(m, {
prefix: Array.isArray(options?.prefix) ? options.prefix : [options?.prefix],
suffix: Array.isArray(options?.suffix) ? options.suffix : [options?.suffix]
}));
}).join(EOL);
return message;
}
style(level, message) {
if (!level || !message) return message;
const icon = this.icon(level, !this.options.useIcons && this.wrap(level));
if (icon) message = icon + " " + message;
return message;
}
applyFormat(message, options) {
if (options?.format) return options.format(message);
return message;
}
spacing(message) {
return typeof message === "undefined" || message.trim() === "" ? "" : " ";
}
};
var ProcessOutputBuffer = class {
buffer = [];
decoder = new StringDecoder();
constructor(options) {
this.options = options;
}
get all() {
return this.buffer;
}
get last() {
return this.buffer.at(-1);
}
get length() {
return this.buffer.length;
}
write(data, ...args) {
const callback = args[args.length - 1];
this.buffer.push({
time: Date.now(),
stream: this.options?.stream,
entry: this.decoder.write(typeof data === "string" ? Buffer.from(data, typeof args[0] === "string" ? args[0] : void 0) : Buffer.from(data))
});
if (this.options?.limit) this.buffer = this.buffer.slice(-this.options.limit);
if (typeof callback === "function") callback();
return true;
}
reset() {
this.buffer = [];
}
};
var ProcessOutputStream = class {
method;
buffer;
constructor(stream) {
this.stream = stream;
this.method = stream.write;
this.buffer = new ProcessOutputBuffer({ stream });
}
get out() {
return Object.assign({}, this.stream, { write: this.write.bind(this) });
}
hijack() {
this.stream.write = this.buffer.write.bind(this.buffer);
}
release() {
this.stream.write = this.method;
const buffer = [...this.buffer.all];
this.buffer.reset();
return buffer;
}
write(...args) {
return this.method.apply(this.stream, args);
}
};
/**
* Creates a new Listr2 process-output controller.
*
* This is used to control the flow to `process.stdout` and `process.stderr` for all renderers.
*
* @see {@link https://listr2.kilic.dev/renderer/process-output.html}
*/
var ProcessOutput = class {
stream;
active;
constructor(stdout, stderr, options) {
this.options = options;
this.stream = {
stdout: new ProcessOutputStream(stdout ?? process.stdout),
stderr: new ProcessOutputStream(stderr ?? process.stderr)
};
this.options = {
dump: ["stdout", "stderr"],
leaveEmptyLine: true,
...options
};
}
get stdout() {
return this.stream.stdout.out;
}
get stderr() {
return this.stream.stderr.out;
}
hijack() {
if (this.active) throw new Error("ProcessOutput has been already hijacked!");
this.stream.stdout.write(ANSI_ESCAPE_CODES.CURSOR_HIDE);
Object.values(this.stream).forEach((stream) => stream.hijack());
this.active = true;
}
release() {
const output = Object.entries(this.stream).map(([name, stream]) => ({
name,
buffer: stream.release()
})).filter((output$1) => this.options.dump.includes(output$1.name)).flatMap((output$1) => output$1.buffer).sort((a, b) => a.time - b.time).map((message) => {
return {
...message,
entry: cleanseAnsi(message.entry)
};
}).filter((message) => message.entry);
if (output.length > 0) {
if (this.options.leaveEmptyLine) this.stdout.write(EOL);
output.forEach((message) => {
const stream = message.stream ?? this.stdout;
stream.write(message.entry + EOL);
});
}
this.stream.stdout.write(ANSI_ESCAPE_CODES.CURSOR_SHOW);
this.active = false;
}
toStdout(buffer, eol = true) {
if (eol) buffer = buffer + EOL;
return this.stream.stdout.write(buffer);
}
toStderr(buffer, eol = true) {
if (eol) buffer = buffer + EOL;
return this.stream.stderr.write(buffer);
}
};
/* istanbul ignore next */
function createWritable(cb) {
const writable = new Writable();
writable.rows = Infinity;
writable.columns = Infinity;
writable.write = (chunk) => {
cb(chunk.toString());
return true;
};
return writable;
}
/* istanbul ignore next */
var Spinner = class {
spinner = !isUnicodeSupported() ? [
"-",
"\\",
"|",
"/"
] : [
"⠋",
"⠙",
"⠹",
"⠸",
"⠼",
"⠴",
"⠦",
"⠧",
"⠇",
"⠏"
];
id;
spinnerPosition = 0;
spin() {
this.spinnerPosition = ++this.spinnerPosition % this.spinner.length;
}
fetch() {
return this.spinner[this.spinnerPosition];
}
isRunning() {
return !!this.id;
}
start(cb, interval = 100) {
this.id = setInterval(() => {
this.spin();
if (cb) cb();
}, interval);
}
stop() {
clearInterval(this.id);
}
};
let ListrDefaultRendererLogLevels = /* @__PURE__ */ function(ListrDefaultRendererLogLevels$1) {
ListrDefaultRendererLogLevels$1["SKIPPED_WITH_COLLAPSE"] = "SKIPPED_WITH_COLLAPSE";
ListrDefaultRendererLogLevels$1["SKIPPED_WITHOUT_COLLAPSE"] = "SKIPPED_WITHOUT_COLLAPSE";
ListrDefaultRendererLogLevels$1["OUTPUT"] = "OUTPUT";
ListrDefaultRendererLogLevels$1["OUTPUT_WITH_BOTTOMBAR"] = "OUTPUT_WITH_BOTTOMBAR";
ListrDefaultRendererLogLevels$1["PENDING"] = "PENDING";
ListrDefaultRendererLogLevels$1["COMPLETED"] = "COMPLETED";
ListrDefaultRendererLogLevels$1["COMPLETED_WITH_FAILED_SUBTASKS"] = "COMPLETED_WITH_FAILED_SUBTASKS";
ListrDefaultRendererLogLevels$1["COMPLETED_WITH_FAILED_SISTER_TASKS"] = "COMPLETED_WITH_SISTER_TASKS_FAILED";
ListrDefaultRendererLogLevels$1["RETRY"] = "RETRY";
ListrDefaultRendererLogLevels$1["ROLLING_BACK"] = "ROLLING_BACK";
ListrDefaultRendererLogLevels$1["ROLLED_BACK"] = "ROLLED_BACK";
ListrDefaultRendererLogLevels$1["FAILED"] = "FAILED";
ListrDefaultRendererLogLevels$1["FAILED_WITH_FAILED_SUBTASKS"] = "FAILED_WITH_SUBTASKS";
ListrDefaultRendererLogLevels$1["WAITING"] = "WAITING";
ListrDefaultRendererLogLevels$1["PAUSED"] = "PAUSED";
return ListrDefaultRendererLogLevels$1;
}({});
const LISTR_DEFAULT_RENDERER_STYLE = {
icon: {
[ListrDefaultRendererLogLevels.SKIPPED_WITH_COLLAPSE]: figures.arrowDown,
[ListrDefaultRendererLogLevels.SKIPPED_WITHOUT_COLLAPSE]: figures.warning,
[ListrDefaultRendererLogLevels.OUTPUT]: figures.pointerSmall,
[ListrDefaultRendererLogLevels.OUTPUT_WITH_BOTTOMBAR]: figures.pointerSmall,
[ListrDefaultRendererLogLevels.PENDING]: figures.pointer,
[ListrDefaultRendererLogLevels.COMPLETED]: figures.tick,
[ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SUBTASKS]: figures.warning,
[ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SISTER_TASKS]: figures.squareSmallFilled,
[ListrDefaultRendererLogLevels.RETRY]: figures.warning,
[ListrDefaultRendererLogLevels.ROLLING_BACK]: figures.warning,
[ListrDefaultRendererLogLevels.ROLLED_BACK]: figures.arrowLeft,
[ListrDefaultRendererLogLevels.FAILED]: figures.cross,
[ListrDefaultRendererLogLevels.FAILED_WITH_FAILED_SUBTASKS]: figures.pointer,
[ListrDefaultRendererLogLevels.WAITING]: figures.squareSmallFilled,
[ListrDefaultRendererLogLevels.PAUSED]: figures.squareSmallFilled
},
color: {
[ListrDefaultRendererLogLevels.SKIPPED_WITH_COLLAPSE]: color.yellow,
[ListrDefaultRendererLogLevels.SKIPPED_WITHOUT_COLLAPSE]: color.yellow,
[ListrDefaultRendererLogLevels.PENDING]: color.yellow,
[ListrDefaultRendererLogLevels.COMPLETED]: color.green,
[ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SUBTASKS]: color.yellow,
[ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SISTER_TASKS]: color.red,
[ListrDefaultRendererLogLevels.RETRY]: color.yellowBright,
[ListrDefaultRendererLogLevels.ROLLING_BACK]: color.redBright,
[ListrDefaultRendererLogLevels.ROLLED_BACK]: color.redBright,
[ListrDefaultRendererLogLevels.FAILED]: color.red,
[ListrDefaultRendererLogLevels.FAILED_WITH_FAILED_SUBTASKS]: color.red,
[ListrDefaultRendererLogLevels.WAITING]: color.dim,
[ListrDefaultRendererLogLevels.PAUSED]: color.yellowBright
}
};
/**
* A basic function to parse minutes and tasks passed given a duration.
* Useful for renderers to show the task time.
*/
/* istanbul ignore next */
function parseTimer(duration) {
const seconds = Math.floor(duration / 1e3);
const minutes = Math.floor(seconds / 60);
let parsedTime;
if (seconds === 0 && minutes === 0) parsedTime = `0.${Math.floor(duration / 100)}s`;
if (seconds > 0) parsedTime = `${seconds % 60}s`;
if (minutes > 0) parsedTime = `${minutes}m${parsedTime}`;
return parsedTime;
}
/* istanbul ignore next */
const PRESET_TIMER = {
condition: true,
field: parseTimer,
format: () => color.dim
};
var DefaultRenderer = class DefaultRenderer$1 {
static nonTTY = false;
static rendererOptions = {
indentation: 2,
clearOutput: false,
showSubtasks: true,
collapseSubtasks: true,
collapseSkips: true,
showSkipMessage: true,
suffixSkips: false,
collapseErrors: true,
showErrorMessage: true,
suffixRetries: true,
lazy: false,
removeEmptyLines: true,
formatOutput: "wrap",
pausedTimer: {
...PRESET_TIMER,
format: () => color.yellowBright
}
};
static rendererTaskOptions = { outputBar: true };
prompt;
activePrompt;
spinner;
logger;
updater;
truncate;
wrap;
buffer = {
output: /* @__PURE__ */ new Map(),
bottom: /* @__PURE__ */ new Map()
};
cache = {
render: /* @__PURE__ */ new Map(),
rendererOptions: /* @__PURE__ */ new Map(),
rendererTaskOptions: /* @__PURE__ */ new Map()
};
constructor(tasks, options, events) {
this.tasks = tasks;
this.options = options;
this.events = events;
this.options = {
...DefaultRenderer$1.rendererOptions,
...this.options,
icon: {
...LISTR_DEFAULT_RENDERER_STYLE.icon,
...options?.icon ?? {}
},
color: {
...LISTR_DEFAULT_RENDERER_STYLE.color,
...options?.color ?? {}
}
};
this.spinner = this.options.spinner ?? new Spinner();
this.logger = this.options.logger ?? new ListrLogger({
useIcons: true,
toStderr: []
});
this.logger.options.icon = this.options.icon;
this.logger.options.color = this.options.color;
}
async render() {
const { createLogUpdate } = await import("./log-update-waQufObt.js");
const { default: truncate } = await import("./cli-truncate-DY-z6Ecp.js");
const { default: wrap } = await import("./wrap-ansi-CVXwzrKr.js");
this.updater = createLogUpdate(this.logger.process.stdout);
this.truncate = truncate;
this.wrap = wrap;
this.logger.process.hijack();
/* istanbul ignore if */
if (!this.options?.lazy) this.spinner.start(() => {
this.update();
});
this.events.on(ListrEventType.SHOULD_REFRESH_RENDER, () => {
this.update();
});
}
update() {
this.updater(this.create());
}
end() {
this.spinner.stop();
this.updater.clear();
this.updater.done();
if (!this.options.clearOutput) this.logger.process.toStdout(this.create({ prompt: false }));
this.logger.process.release();
}
create(options) {
options = {
tasks: true,
bottomBar: true,
prompt: true,
...options
};
const render = [];
const renderTasks = this.renderer(this.tasks);
const renderBottomBar = this.renderBottomBar();
const renderPrompt = this.renderPrompt();
if (options.tasks && renderTasks.length > 0) render.push(...renderTasks);
if (options.bottomBar && renderBottomBar.length > 0) {
if (render.length > 0) render.push("");
render.push(...renderBottomBar);
}
if (options.prompt && renderPrompt.length > 0) {
if (render.length > 0) render.push("");
render.push(...renderPrompt);
}
return render.join(EOL);
}
style(task, output = false) {
const rendererOptions = this.cache.rendererOptions.get(task.id);
if (task.isSkipped()) {
if (output || rendererOptions.collapseSkips) return this.logger.icon(ListrDefaultRendererLogLevels.SKIPPED_WITH_COLLAPSE);
else if (rendererOptions.collapseSkips === false) return this.logger.icon(ListrDefaultRendererLogLevels.SKIPPED_WITHOUT_COLLAPSE);
}
if (output) {
if (this.shouldOutputToBottomBar(task)) return this.logger.icon(ListrDefaultRendererLogLevels.OUTPUT_WITH_BOTTOMBAR);
return this.logger.icon(ListrDefaultRendererLogLevels.OUTPUT);
}
if (task.hasSubtasks()) {
if (task.isStarted() || task.isPrompt() && rendererOptions.showSubtasks !== false && !task.subtasks.every((subtask) => !subtask.hasTitle())) return this.logger.icon(ListrDefaultRendererLogLevels.PENDING);
else if (task.isCompleted() && task.subtasks.some((subtask) => subtask.hasFailed())) return this.logger.icon(ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SUBTASKS);
else if (task.hasFailed()) return this.logger.icon(ListrDefaultRendererLogLevels.FAILED_WITH_FAILED_SUBTASKS);
}
if (task.isStarted() || task.isPrompt()) return this.logger.icon(ListrDefaultRendererLogLevels.PENDING, !this.options?.lazy && this.spinner.fetch());
else if (task.isCompleted()) return this.logger.icon(ListrDefaultRendererLogLevels.COMPLETED);
else if (task.isRetrying()) return this.logger.icon(ListrDefaultRendererLogLevels.RETRY, !this.options?.lazy && this.spinner.fetch());
else if (task.isRollingBack()) return this.logger.icon(ListrDefaultRendererLogLevels.ROLLING_BACK, !this.options?.lazy && this.spinner.fetch());
else if (task.hasRolledBack()) return this.logger.icon(ListrDefaultRendererLogLevels.ROLLED_BACK);
else if (task.hasFailed()) return this.logger.icon(ListrDefaultRendererLogLevels.FAILED);
else if (task.isPaused()) return this.logger.icon(ListrDefaultRendererLogLevels.PAUSED);
return this.logger.icon(ListrDefaultRendererLogLevels.WAITING);
}
format(message, icon, level) {
if (message.trim() === "") return [];
if (icon) message = icon + " " + message;
let parsed;
const columns = (process.stdout.columns ?? 80) - level * this.options.indentation - 2;
switch (this.options.formatOutput) {
case "truncate":
parsed = message.split(EOL).map((s, i) => {
return this.truncate(this.indent(s, i), columns);
});
break;
case "wrap":
parsed = this.wrap(message, columns, {
hard: true,
trim: false
}).split(EOL).map((s, i) => this.indent(s, i));
break;
default: throw new ListrRendererError("Format option for the renderer is wrong.");
}
if (this.options.removeEmptyLines) parsed = parsed.filter(Boolean);
return parsed.map((str) => indent(str, level * this.options.indentation));
}
shouldOutputToOutputBar(task) {
const outputBar = this.cache.rendererTaskOptions.get(task.id).outputBar;
return typeof outputBar === "number" && outputBar !== 0 || typeof outputBar === "boolean" && outputBar !== false;
}
shouldOutputToBottomBar(task) {
const bottomBar = this.cache.rendererTaskOptions.get(task.id).bottomBar;
return typeof bottomBar === "number" && bottomBar !== 0 || typeof bottomBar === "boolean" && bottomBar !== false || !task.hasTitle();
}
renderer(tasks, level = 0) {
return tasks.flatMap((task) => {
if (!task.isEnabled()) return [];
if (this.cache.render.has(task.id)) return this.cache.render.get(task.id);
this.calculate(task);
this.setupBuffer(task);
const rendererOptions = this.cache.rendererOptions.get(task.id);
const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id);
const output = [];
if (task.isPrompt()) {
if (this.activePrompt && this.activePrompt !== task.id) throw new ListrRendererError("Only one prompt can be active at the given time, please re-evaluate your task design.");
else if (!this.activePrompt) {
task.on(ListrTaskEventType.PROMPT, (prompt) => {
const cleansed = cleanseAnsi(prompt);
if (cleansed) this.prompt = cleansed;
});
task.on(ListrTaskEventType.STATE, (state) => {
if (state === ListrTaskState.PROMPT_COMPLETED || task.hasFinalized() || task.hasReset()) {
this.prompt = null;
this.activePrompt = null;
task.off(ListrTaskEventType.PROMPT);
}
});
this.activePrompt = task.id;
}
}
if (task.hasTitle()) if (!(tasks.some((task$1) => task$1.hasFailed()) && !task.hasFailed() && task.options.exitOnError !== false && !(task.isCompleted() || task.isSkipped()))) if (task.hasFailed() && rendererOptions.collapseErrors) output.push(...this.format(!task.hasSubtasks() && task.message.error && rendererOptions.showErrorMessage ? task.message.error : task.title, this.style(task), level));
else if (task.isSkipped() && rendererOptions.collapseSkips) output.push(...this.format(this.logger.suffix(task.message.skip && rendererOptions.showSkipMessage ? task.message.skip : task.title, {
field: ListrLogLevels.SKIPPED,
condition: rendererOptions.suffixSkips,
format: () => color.dim
}), this.style(task), level));
else if (task.isRetrying()) output.push(...this.format(this.logger.suffix(task.title, {
field: `${ListrLogLevels.RETRY}:${task.message.retry.count}`,
format: () => color.yellow,
condition: rendererOptions.suffixRetries
}), this.style(task), level));
else if (task.isCompleted() && task.hasTitle() && assertFunctionOrSelf(rendererTaskOptions.timer?.condition, task.message.duration)) output.push(...this.format(this.logger.suffix(task?.title, {
...rendererTaskOptions.timer,
args: [task.message.duration]
}), this.style(task), level));
else if (task.isPaused()) output.push(...this.format(this.logger.suffix(task.title, {
...rendererOptions.pausedTimer,
args: [task.message.paused - Date.now()]
}), this.style(task), level));
else output.push(...this.format(task.title, this.style(task), level));
else output.push(...this.format(task.title, this.logger.icon(ListrDefaultRendererLogLevels.COMPLETED_WITH_FAILED_SISTER_TASKS), level));
if (!task.hasSubtasks() || !rendererOptions.showSubtasks) {
if (task.hasFailed() && rendererOptions.collapseErrors === false && (rendererOptions.showErrorMessage || !rendererOptions.showSubtasks)) output.push(...this.dump(task, level, ListrLogLevels.FAILED));
else if (task.isSkipped() && rendererOptions.collapseSkips === false && (rendererOptions.showSkipMessage || !rendererOptions.showSubtasks)) output.push(...this.dump(task, level, ListrLogLevels.SKIPPED));
}
if (task.isPending() || rendererTaskOptions.persistentOutput) output.push(...this.renderOutputBar(task, level));
if (rendererOptions.showSubtasks !== false && task.hasSubtasks() && (task.isPending() || task.hasFinalized() && !task.hasTitle() || task.isCompleted() && rendererOptions.collapseSubtasks === false && !task.subtasks.some((subtask) => this.cache.rendererOptions.get(subtask.id)?.collapseSubtasks === true) || task.subtasks.some((subtask) => this.cache.rendererOptions.get(subtask.id)?.collapseSubtasks === false) || task.subtasks.some((subtask) => subtask.hasFailed()) || task.subtasks.some((subtask) => subtask.hasRolledBack()))) {
const subtaskLevel = !task.hasTitle() ? level : level + 1;
const subtaskRender = this.renderer(task.subtasks, subtaskLevel);
output.push(...subtaskRender);
}
if (task.hasFinalized()) {
if (!rendererTaskOptions.persistentOutput) {
this.buffer.bottom.delete(task.id);
this.buffer.output.delete(task.id);
}
}
if (task.isClosed()) {
this.cache.render.set(task.id, output);
this.reset(task);
}
return output;
});
}
renderOutputBar(task, level) {
const output = this.buffer.output.get(task.id);
if (!output) return [];
return output.all.flatMap((o) => this.dump(task, level, ListrLogLevels.OUTPUT, o.entry));
}
renderBottomBar() {
if (this.buffer.bottom.size === 0) return [];
return Array.from(this.buffer.bottom.values()).flatMap((output) => output.all).sort((a, b) => a.time - b.time).map((output) => output.entry);
}
renderPrompt() {
if (!this.prompt) return [];
return [this.prompt];
}
calculate(task) {
if (this.cache.rendererOptions.has(task.id) && this.cache.rendererTaskOptions.has(task.id)) return;
const rendererOptions = {
...this.options,
...task.rendererOptions
};
this.cache.rendererOptions.set(task.id, rendererOptions);
this.cache.rendererTaskOptions.set(task.id, {
...DefaultRenderer$1.rendererTaskOptions,
timer: rendererOptions.timer,
...task.rendererTaskOptions
});
}
setupBuffer(task) {
if (this.buffer.bottom.has(task.id) || this.buffer.output.has(task.id)) return;
const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id);
if (this.shouldOutputToBottomBar(task) && !this.buffer.bottom.has(task.id)) {
this.buffer.bottom.set(task.id, new ProcessOutputBuffer({ limit: typeof rendererTaskOptions.bottomBar === "number" ? rendererTaskOptions.bottomBar : 1 }));
task.on(ListrTaskEventType.OUTPUT, (output) => {
const data = this.dump(task, -1, ListrLogLevels.OUTPUT, output);
this.buffer.bottom.get(task.id).write(data.join(EOL));
});
task.on(ListrTaskEventType.STATE, (state) => {
switch (state) {
case ListrTaskState.RETRY || ListrTaskState.ROLLING_BACK:
this.buffer.bottom.delete(task.id);
break;
}
});
} else if (this.shouldOutputToOutputBar(task) && !this.buffer.output.has(task.id)) {
this.buffer.output.set(task.id, new ProcessOutputBuffer({ limit: typeof rendererTaskOptions.outputBar === "number" ? rendererTaskOptions.outputBar : 1 }));
task.on(ListrTaskEventType.OUTPUT, (output) => {
this.buffer.output.get(task.id).write(output);
});
task.on(ListrTaskEventType.STATE, (state) => {
switch (state) {
case ListrTaskState.RETRY || ListrTaskState.ROLLING_BACK:
this.buffer.output.delete(task.id);
break;
}
});
}
}
reset(task) {
this.cache.rendererOptions.delete(task.id);
this.cache.rendererTaskOptions.delete(task.id);
this.buffer.output.delete(task.id);
}
dump(task, level, source = ListrLogLevels.OUTPUT, data) {
if (!data) switch (source) {
case ListrLogLevels.OUTPUT:
data = task.output;
break;
case ListrLogLevels.SKIPPED:
data = task.message.skip;
break;
case ListrLogLevels.FAILED:
data = task.message.error;
break;
}
if (task.hasTitle() && source === ListrLogLevels.FAILED && data === task.title || typeof data !== "string") return [];
if (source === ListrLogLevels.OUTPUT) data = cleanseAnsi(data);
return this.format(data, this.style(task, true), level + 1);
}
indent(str, i) {
return i > 0 ? indent(str.trimEnd(), this.options.indentation) : str.trimEnd();
}
};
var SilentRenderer = class {
static nonTTY = true;
static rendererOptions;
static rendererTaskOptions;
constructor(tasks, options) {
this.tasks = tasks;
this.options = options;
}
render() {
return;
}
end() {
return;
}
};
var SimpleRenderer = class SimpleRenderer$1 {
static nonTTY = true;
static rendererOptions = { pausedTimer: {
...PRESET_TIMER,
field: (time) => `${ListrLogLevels.PAUSED}:${time}`,
format: () => color.yellowBright
} };
static rendererTaskOptions = {};
logger;
cache = {
rendererOptions: /* @__PURE__ */ new Map(),
rendererTaskOptions: /* @__PURE__ */ new Map()
};
constructor(tasks, options) {
this.tasks = tasks;
this.options = options;
this.options = {
...SimpleRenderer$1.rendererOptions,
...options,
icon: {
...LISTR_LOGGER_STYLE.icon,
...options?.icon ?? {}
},
color: {
...LISTR_LOGGER_STYLE.color,
...options?.color ?? {}
}
};
this.logger = this.options.logger ?? new ListrLogger({
useIcons: true,
toStderr: LISTR_LOGGER_STDERR_LEVELS
});
this.logger.options.icon = this.options.icon;
this.logger.options.color = this.options.color;
if (this.options.timestamp) this.logger.options.fields.prefix.unshift(this.options.timestamp);
}
end() {}
render() {
this.renderer(this.tasks);
}
renderer(tasks) {
tasks.forEach((task) => {
this.calculate(task);
task.once(ListrTaskEventType.CLOSED, () => {
this.reset(task);
});
const rendererOptions = this.cache.rendererOptions.get(task.id);
const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id);
task.on(ListrTaskEventType.SUBTASK, (subtasks) => {
this.renderer(subtasks);
});
task.on(ListrTaskEventType.STATE, (state) => {
if (!task.hasTitle()) return;
if (state === ListrTaskState.STARTED) this.logger.log(ListrLogLevels.STARTED, task.title);
else if (state === ListrTaskState.COMPLETED) {
const timer = rendererTaskOptions?.timer;
this.logger.log(ListrLogLevels.COMPLETED, task.title, timer && { suffix: {
...timer,
condition: !!task.message?.duration && timer.condition,
args: [task.message.duration]
} });
} else if (state === ListrTaskState.PROMPT) {
this.logger.process.hijack();
task.on(ListrTaskEventType.PROMPT, (prompt) => {
this.logger.process.toStderr(prompt, false);
});
} else if (state === ListrTaskState.PROMPT_COMPLETED) {
task.off(ListrTaskEventType.PROMPT);
this.logger.process.release();
}
});
task.on(ListrTaskEventType.OUTPUT, (output) => {
this.logger.log(ListrLogLevels.OUTPUT, output);
});
task.on(ListrTaskEventType.MESSAGE, (message) => {
if (message.error) this.logger.log(ListrLogLevels.FAILED, task.title, { suffix: {
field: `${ListrLogLevels.FAILED}: ${message.error}`,
format: () => color.red
} });
else if (message.skip) this.logger.log(ListrLogLevels.SKIPPED, task.title, { suffix: {
field: `${ListrLogLevels.SKIPPED}: ${message.skip}`,
format: () => color.yellow
} });
else if (message.rollback) this.logger.log(ListrLogLevels.ROLLBACK, task.title, { suffix: {
field: `${ListrLogLevels.ROLLBACK}: ${message.rollback}`,
format: () => color.red
} });
else if (message.retry) this.logger.log(ListrLogLevels.RETRY, task.title, { suffix: {
field: `${ListrLogLevels.RETRY}:${message.retry.count}`,
format: () => color.red
} });
else if (message.paused) {
const timer = rendererOptions?.pausedTimer;
this.logger.log(ListrLogLevels.PAUSED, task.title, timer && { suffix: {
...timer,
condition: !!message?.paused && timer.condition,
args: [message.paused - Date.now()]
} });
}
});
});
}
calculate(task) {
if (this.cache.rendererOptions.has(task.id) && this.cache.rendererTaskOptions.has(task.id)) return;
const rendererOptions = {
...this.options,
...task.rendererOptions
};
this.cache.rendererOptions.set(task.id, rendererOptions);
this.cache.rendererTaskOptions.set(task.id, {
...SimpleRenderer$1.rendererTaskOptions,
timer: rendererOptions.timer,
...task.rendererTaskOptions
});
}
reset(task) {
this.cache.rendererOptions.delete(task.id);
this.cache.rendererTaskOptions.delete(task.id);
}
};
var TestRendererSerializer = class {
constructor(options) {
this.options = options;
}
serialize(event, data, task) {
return JSON.stringify(this.generate(event, data, task));
}
generate(event, data, task) {
const output = {
event,
data
};
if (typeof this.options?.task !== "boolean") {
const t = Object.fromEntries(this.options.task.map((entity) => {
const property = task[entity];
if (typeof property === "function") return [entity, property.call(task)];
return [entity, property];
}));
if (Object.keys(task).length > 0) output.task = t;
}
return output;
}
};
var TestRenderer = class TestRenderer$1 {
static nonTTY = true;
static rendererOptions = {
subtasks: true,
state: Object.values(ListrTaskState),
output: true,
prompt: true,
title: true,
messages: [
"skip",
"error",
"retry",
"rollback",
"paused"
],
messagesToStderr: [
"error",
"rollback",
"retry"
],
task: [
"hasRolledBack",
"isRollingBack",
"isCompleted",
"isSkipped",
"hasFinalized",
"hasSubtasks",
"title",
"hasReset",
"hasTitle",
"isPrompt",
"isPaused",
"isPending",
"isSkipped",
"isStarted",
"hasFailed",
"isEnabled",
"isRetrying",
"path"
]
};
static rendererTaskOptions;
logger;
serializer;
constructor(tasks, options) {
this.tasks = tasks;
this.options = options;
this.options = {
...TestRenderer$1.rendererOptions,
...this.options
};
this.logger = this.options.logger ?? new ListrLogger({ useIcons: false });
this.serializer = new TestRendererSerializer(this.options);
}
render() {
this.renderer(this.tasks);
}
end() {}
renderer(tasks) {
tasks.forEach((task) => {
if (this.options.subtasks) task.on(ListrTaskEventType.SUBTASK, (subtasks) => {
this.renderer(subtasks);
});
if (this.options.state) task.on(ListrTaskEventType.STATE, (state) => {
this.logger.toStdout(this.serializer.serialize(ListrTaskEventType.STATE, state, task));
});
if (this.options.output) task.on(ListrTaskEventType.OUTPUT, (data) => {
this.logger.toStdout(this.serializer.serialize(ListrTaskEventType.OUTPUT, data, task));
});
if (this.options.prompt) task.on(ListrTaskEventType.PROMPT, (prompt) => {
this.logger.toStdout(this.serializer.serialize(ListrTaskEventType.PROMPT, prompt, task));
});
if (this.options.title) task.on(ListrTaskEventType.TITLE, (title) => {
this.logger.toStdout(this.serializer.serialize(ListrTaskEventType.TITLE, title, task));
});
task.on(ListrTaskEventType.MESSAGE, (message) => {
const parsed = Object.fromEntries(Object.entries(message).map(([key, value]) => {
if (this.options.messages.includes(key)) return [key, value];
}).filter(Boolean));
if (Object.keys(parsed).length > 0) {
const output = this.serializer.serialize(ListrTaskEventType.MESSAGE, parsed, task);
if (this.options.messagesToStderr.some((state) => Object.keys(parsed).includes(state))) this.logger.toStderr(output);
else this.logger.toStdout(output);
}
});
});
}
};
var VerboseRenderer = class VerboseRenderer$1 {
static nonTTY = true;
static rendererOptions = {
logTitleChange: false,
pausedTimer: {
...PRESET_TIMER,
format: () => color.yellowBright
}
};
static rendererTaskOptions;
logger;
cache = {
rendererOptions: /* @__PURE__ */ new Map(),
rendererTaskOptions: /* @__PURE__ */ new Map()
};
constructor(tasks, options) {
this.tasks = tasks;
this.options = options;
this.options = {
...VerboseRenderer$1.rendererOptions,
...this.options,
icon: {
...LISTR_LOGGER_STYLE.icon,
...options?.icon ?? {}
},
color: {
...LISTR_LOGGER_STYLE.color,
...options?.color ?? {}
}
};
this.logger = this.options.logger ?? new ListrLogger({
useIcons: false,
toStderr: LISTR_LOGGER_STDERR_LEVELS
});
this.logger.options.icon = this.options.icon;
this.logger.options.color = this.options.color;
if (this.options.timestamp) this.logger.options.fields.prefix.unshift(this.options.timestamp);
}
render() {
this.renderer(this.tasks);
}
end() {}
renderer(tasks) {
tasks.forEach((task) => {
this.calculate(task);
task.once(ListrTaskEventType.CLOSED, () => {
this.reset(task);
});
const rendererOptions = this.cache.rendererOptions.get(task.id);
const rendererTaskOptions = this.cache.rendererTaskOptions.get(task.id);
task.on(ListrTaskEventType.SUBTASK, (subtasks) => {
this.renderer(subtasks);
});
task.on(ListrTaskEventType.STATE, (state) => {
if (!task.hasTitle()) return;
if (state === ListrTaskState.STARTED) this.logger.log(ListrLogLevels.STARTED, task.title);
else if (state === ListrTaskState.COMPLETED) {
const timer = rendererTaskOptions.timer;
this.logger.log(ListrLogLevels.COMPLETED, task.title, timer && { suffix: {
...timer,
condition: !!task.message?.duration && timer.condition,
args: [task.message.duration]
} });
}
});
task.on(ListrTaskEventType.OUTPUT, (data) => {
this.logger.log(ListrLogLevels.OUTPUT, data);
});
task.on(ListrTaskEventType.PROMPT, (prompt) => {
const cleansed = cleanseAnsi(prompt);
if (cleansed) this.logger.log(ListrLogLevels.PROMPT, cleansed);
});
if (this.options?.logTitleChange !== false) task.on(ListrTaskEventType.TITLE, (title) => {
this.logger.log(ListrLogLevels.TITLE, title);
});
task.on(ListrTaskEventType.MESSAGE, (message) => {
if (message?.error) this.logger.log(ListrLogLevels.FAILED, message.error);
else if (message?.skip) this.logger.log(ListrLogLevels.SKIPPED, message.skip);
else if (message?.rollback) this.logger.log(ListrLogLevels.ROLLBACK, message.rollback);
else if (message?.retry) this.logger.log(ListrLogLevels.RETRY, task.title, { suffix: message.retry.count.toString() });
else if (message?.paused) {
const timer = rendererOptions?.pausedTimer;
this.logger.log(ListrLogLevels.PAUSED, task.title, timer && { suffix: {
...timer,
condition: !!message?.paused && timer.condition,
args: [message.paused - Date.now()]
} });
}
});
});
}
calculate(task) {
if (this.cache.rendererOptions.has(task.id) && this.cache.rendererTaskOptions.has(task.id)) return;
const rendererOptions = {
...this.options,
...task.rendererOptions
};
this.cache.rendererOptions.set(task.id, rendererOptions);
this.cache.rendererTaskOptions.set(task.id, {
...VerboseRenderer$1.rendererTaskOptions,
timer: rendererOptions.timer,
...task.rendererTaskOptions
});
}
reset(task) {
this.cache.rendererOptions.delete(task.id);
this.cache.rendererTaskOptions.delete(task.id);
}
};
const RENDERERS = {
default: DefaultRenderer,
simple: SimpleRenderer,
verbose: VerboseRenderer,
test: TestRenderer,
silent: SilentRenderer
};
function isRendererSupported(renderer) {
return process.stdout.isTTY === true || renderer.nonTTY === true;
}
function getRendererClass(renderer) {
if (typeof renderer === "string") return RENDERERS[renderer] ?? RENDERERS.default;
return typeof renderer === "function" ? renderer : RENDERERS.default;
}
function getRenderer(options) {
if (assertFunctionOrSelf(options?.silentRendererCondition)) return {
renderer: getRendererClass("silent"),
selection: ListrRendererSelection.SILENT
};
const r = {
renderer: getRendererClass(options.renderer),
options: options.rendererOptions,
selection: ListrRendererSelection.PRIMARY
};
if (!isRendererSupported(r.renderer) || assertFunctionOrSelf(options?.fallbackRendererCondition)) return {
renderer: getRendererClass(options.fallbackRenderer),
options: options.fallbackRendererOptions,
selection: ListrRendererSelection.SECONDARY
};
return r;
}
/**
* This function asserts the given value as a function or itself.
* If the value itself is a function it will evaluate it with the passed in arguments,
* elsewise it will directly return itself.
*/
function assertFunctionOrSelf(functionOrSelf, ...args) {
if (typeof functionOrSelf === "function") return functionOrSelf(...args);
else return functionOrSelf;
}
const clone = (0, import_rfdc.default)({ circles: true });
/**
* Deep clones a object in the easiest manner.
*/
function cloneObject(obj) {
return clone(obj);
}
var Concurrency = class {
concurrency;
count;
queue;
constructor(options) {
this.concurrency = options.concurrency;
this.count = 0;
this.queue = /* @__PURE__ */ new Set();
}
add(fn) {
if (this.count < this.concurrency) return this.run(fn);
return new Promise((resolve) => {
const callback = () => resolve(this.run(fn));
this.queue.add(callback);
});
}
flush() {
for (const callback of this.queue) {
if (this.count >= this.concurrency) break;
this.queue.delete(callback);
callback();
}
}
run(fn) {
this.count++;
const promise = fn();
const cleanup = () => {
this.count--;
this.flush();
};
promise.then(cleanup, () => {
this.queue.clear();
});
return promise;
}
};
function delay(time) {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
}
/**
* Internal error handling mechanism for Listr collects the errors and details for a failed task.
*
* @see {@link https://listr2.kilic.dev/task/error-handling.html}
*/
var ListrError = class extends Error {
path;
ctx;
constructor(error, type, task) {
super(error.message);
this.error = error;
this.type = type;
this.task = task;
this.name = "ListrError";
this.path = task.path;
if (task?.options.collectErrors === "full") {
this.task = cloneObject(task);
this.ctx = cloneObject(task.listr.ctx);
}
this.stack = error?.stack;
}
};
/**
* Internal error coming from renderer.
*/
var ListrRendererError = class extends Error {};
/**
* Internal error handling mechanism for Listr prompts to identify the failing cause is coming from a prompt.
*
* @see {@link https://listr2.kilic.dev/task/prompts.html}
*/
var PromptError = class extends Error {};
/**
* The original Task that is defined by the user is wrapped with the TaskWrapper to provide additional functionality.
*
* @see {@link https://listr2.kilic.dev/task/task.html}
*/
var TaskWrapper = class {
constructor(task) {
this.task = task;
}
/* istanbul ignore next */
get title() {
return this.task.title;
}
/**
* Title of the current task.
*
* @see {@link https://listr2.kilic.dev/task/title.html}
*/
set title(title) {
title = Array.isArray(title) ? title : [title];
this.task.title$ = splat(title.shift(), ...title);
}
/* istanbul ignore next */
get output() {
return this.task.output;
}
/* istanbul ignore next */
/**
* Send output from the current task to the renderer.
*
* @see {@link https://listr2.kilic.dev/task/output.html}
*/
set output(output) {
output = Array.isArray(output) ? output : [output];
this.task.output$ = splat(output.shift(), ...output);
}
/* istanbul ignore next */
/** Send an output to the output channel as prompt. */
set promptOutput(output) {
this.task.promptOutput$ = output;
}
/**
* Creates a new set of Listr subtasks.
*
* @see {@link https://listr2.kilic.dev/task/subtasks.html}
*/
newListr(task, options) {
let tasks;
if (typeof task === "function") tasks = task(this);
else tasks = task;
return new Listr(tasks, options, this.task);
}
/**
* Report an error that has to be collected and handled.
*
* @see {@link https://listr2.kilic.dev/task/error-handling.html}
*/
report(error, type) {
if (this.task.options.collectErrors !== false) this.task.listr.errors.push(new ListrError(error, type, this.task));
this.task.message$ = { error: error.message ?? this.task?.title };
}
/**
* Skip the current task.
*
* @see {@link https://listr2.kilic.dev/task/skip.html}
*/
skip(message, ...metadata) {
this.task.state$ = ListrTaskState.SKIPPED;
if (message) this.task.message$ = { skip: message ? splat(message, ...metadata) : this.task?.title };
}
/**
* Check whether this task is currently in a retry state.
*
* @see {@link https://listr2.kilic.dev/task/retry.html}
*/
isRetrying() {
return this.task.isRetrying() ? this.task.retry : { count: 0 };
}
/* istanbul ignore next */
/**
* Create a new prompt for getting user input through the prompt adapter.
* This will create a new prompt through the adapter if the task is not currently rendering a prompt or will return the active instance.
*
* This part of the application requires optional peer dependencies, please refer to documentation.
*
* @see {@link https://listr2.kilic.dev/task/prompt.html}
*/
prompt(adapter) {
if (this.task.prompt) return this.task.prompt;
return new adapter(this.task, this);
}
/* istanbul ignore next */
/**
* Generates a fake stdout for your use case, where it will be tunnelled through Listr to handle the rendering process.
*
* @see {@link https://listr2.kilic.dev/renderer/process-output.html}
*/
stdout(type) {
return createWritable((chunk) => {
switch (type) {
case ListrTaskEventType.PROMPT:
this.promptOutput = chunk;
break;
default: this.output = chunk;
}
});
}
/** Run this task. */
run(ctx) {
return this.task.run(ctx, this);
}
};
var ListrTaskEventManager = class extends EventManager {};
/**
* Creates and handles a runnable instance of the Task.
*/
var Task = class extends ListrTaskEventManager {
/** Unique id per task, can be used for identifying a Task. */
id = randomUUID();
/** The current state of the task. */
state = ListrTaskState.WAITING;
/** Subtasks of the current task. */
subtasks;
/** Title of the task. */
title;
/** Initial/Untouched version of the title for using whenever task has a reset. */
initialTitle;
/** Output channel for the task. */
output;
/** Current state of the retry process whenever the task is retrying. */
retry;
/**
* A channel for messages.
*
* This requires a separate channel for messages like error, skip or runtime messages to further utilize in the renderers.
*/
message = {};
/** Current prompt instance or prompt error whenever the task is prompting. */
prompt;
/** Parent task of the current task. */
parent;
/** Enable flag of this task. */
enabled;
/** User provided Task callback function to run. */
taskFn;
/** Marks the task as closed. This is different from finalized since this is not really related to task itself. */
closed;
constructor(listr, task, options, rendererOptions, rendererTaskOptions) {
super();
this.listr = listr;
this.task = task;
this.options = options;
this.rendererOptions = rendererOptions;
this.rendererTaskOptions = rendererTaskOptions;
if (task.title) {
const title = Array.isArray(task?.title) ? task.title : [task.title];
this.title = splat(title.shift(), ...title);
this.initialTitle = this.title;
}
this.taskFn = task.task;
this.parent = listr.parentTask;
}
/**
* Update the current state of the Task and emit the neccassary events.
*/
set state$(state) {
this.state = state;
this.emit(ListrTaskEventType.STATE, state);
if (this.hasSubtasks() && this.hasFailed()) {
for (const subtask of this.subtasks) if (subtask.state === ListrTaskState.STARTED) subtask.state$ = ListrTaskState.FAILED;
}
this.listr.events.emit(ListrEventType.SHOULD_REFRESH_RENDER);
}
/**
* Update the current output of the Task and emit the neccassary events.
*/
set output$(data) {
this.output = data;
this.emit(ListrTaskEventType.OUTPUT, data);
this.listr.events.emit(ListrEventType.SHOULD_REFRESH_RENDER);
}
/**
* Update the current prompt output of the Task and emit the neccassary events.
*/
set promptOutput$(data) {
this.emit(ListrTaskEventType.PROMPT, data);
if (cleanseAnsi(data)) this.listr.events.emit(ListrEventType.SHOULD_REFRESH_RENDER);
}
/**
* Update or extend the current message of the Task and emit the neccassary events.
*/
set message$(data) {
this.message = {
...this.message,
...data
};
this.emit(ListrTaskEventType.MESSAGE, data);
this.listr.events.emit(ListrEventType.SHOULD_REFRESH_RENDER);
}
/**
* Update the current title of the Task and emit the neccassary events.
*/
set title$(title) {
this.title = title;
this.emit(ListrTaskEventType.TITLE, title);
this.listr.events.emit(ListrEventType.SHOULD_REFRESH_RENDER);
}
/**
* Current task path in the hierarchy.
*/
get path() {
return [...this.listr.path, this.initialTitle];
}
/**
* Checks whether the current task with the given context should be set as enabled.
*/
async check(ctx) {
if (this.state === ListrTaskState.WAITING) {
this.enabled = await assertFunctionOrSelf(this.task?.enabled ?? true, ctx);
this.emit(ListrTaskEventType.ENABLED, this.enabled);
this.listr.events.emit(ListrEventType.SHOULD_REFRESH_RENDER);
}
return this.enabled;
}
/** Returns whether this task has subtasks. */
hasSubtasks() {
return this.subtasks?.length > 0;
}
/** Returns whether this task is finalized in someform. */
hasFinalized() {
return this.isCompleted() || this.hasFailed() || this.isSkipped() || this.hasRolledBack();
}
/** Returns whether this task is in progress. */
isPending() {
return this.isStarted() || this.isPrompt() || this.hasReset();
}
/** Returns whether this task has started. */
isStarted() {
return this.state === ListrTaskState.STARTED;
}
/** Returns whether this task is skipped. */
isSkipped() {
return this.state === ListrTaskState.SKIPPED;
}
/** Returns whether this task has been completed. */
isCompleted() {
return this.state === ListrTaskState.COMPLETED;
}
/** Returns whether this task has been failed. */
hasFailed() {
return this.state === ListrTaskState.FAILED;
}
/** Returns whether this task has an active rollback task going on. */
isRollingBack() {
return this.state === ListrTaskState.ROLLING_BACK;
}
/** Returns whether the rollback action was successful. */
hasRolledBack() {
return this.state === ListrTaskState.ROLLED_BACK;
}
/** Returns whether this task has an actively retrying task going on. */
isRetrying() {
return this.state === ListrTaskState.RETRY;
}
/** Returns whether this task has some kind of reset like retry and rollback going on. */
hasReset() {
return this.state === ListrTaskState.RETRY || this.state === ListrTaskState.ROLLING_BACK;
}
/** Returns whether enabled function resolves to true. */
isEnabled() {
return this.enabled;
}
/** Returns whether this task actually has a title. */
hasTitle() {
return typeof this?.title === "string";
}
/** Returns whether this task has a prompt inside. */
isPrompt() {
return this.state === ListrTaskState.PROMPT || this.state === ListrTaskState.PROMPT_COMPLETED;
}
/** Returns whether this task is currently paused. */
isPaused() {
return this.state === ListrTaskState.PAUSED;
}
/** Returns whether this task is closed. */
isClosed() {
return this.closed;
}
/** Pause the given task for certain time. */
async pause(time) {
const state = this.state;
this.state$ = ListrTaskState.PAUSED;
this.message$ = { paused: Date.now() + time };
await delay(time);
this.state$ = state;
this.message$ = { paused: null };
}
/** Run the current task. */
async run(context, wrapper) {
const handleResult = (result) => {
if (result instanceof Listr) {
result.options = {
...this.options,
...result.options
};
result.rendererClass = getRendererClass("silent");
this.subtasks = result.tasks;
result.errors = this.listr.errors;
this.emit(ListrTaskEventType.SUBTASK, this.subtasks);
result = result.run(context);
} else if (result instanceof Promise) result = result.then(handleResult);
else if (isReadable(result)) result = new Promise((resolve, reject) => {
result.on("data", (data) => {
this.output$ = data.toString();
});
result.on("error", (error) => reject(error));
result.on("end", () => resolve(null));
});
else if (isObservable(result)) result = new Promise((resolve, reject) => {
result.subscribe({
next: (data) => {
this.output$ = data;
},
error: reject,
complete: resolve
});
});
return result;
};
const startTime = Date.now();
this.state$ = ListrTaskState.STARTED;
const skipped = await assertFunctionOrSelf(this.task?.skip ?? false, context);
if (skipped) {
if (typeof skipped === "string") this.message$ = { skip: skipped };
else if (this.hasTitle()) this.message$ = { skip: this.title };
else this.message$ = { skip: "Skipped task without a title." };
this.state$ = ListrTaskState.SKIPPED;
return;
}
try {
const retryCount = typeof this.task?.retry === "number" && this.task.retry > 0 ? this.task.retry + 1 : typeof this.task?.retry === "object" && this.task.retry.tries > 0 ? this.task.retry.tries + 1 : 1;
const retryDelay = typeof this.task.retry === "object" && this.task.retry.delay;
for (let retries = 1; retries <= retryCount; retries++) try {
await handleResult(this.taskFn(context, wrapper));
break;
} catch (err) {
if (retries !== retryCount) {
this.retry = {
count: retries,
error: err
};
this.message$ = { retry: this.retry };
this.title$ = this.initialTitle;
this.output = void 0;
wrapper.report(err, ListrErrorTypes.WILL_RETRY);
this.state$ = ListrTaskState.RETRY;
if (retryDelay) await this.pause(retryDelay);
} else throw err;
}
if (this.isStarted() || this.isRetrying()) {
this.message$ = { duration: Date.now() - startTime };
this.state$ = ListrTaskState.COMPLETED;
}
} catch (error) {
if (this.prompt instanceof PromptError) error = this.prompt;
if (this.task?.rollback) {
wrapper.report(error, ListrErrorTypes.WILL_ROLLBACK);
try {
this.state$ = ListrTaskState.ROLLING_BACK;
await this.task.rollback(context, wrapper);
this.message$ = { rollback: this.title };
this.state$ = ListrTaskState.ROLLED_BACK;
} catch (err) {
this.state$ = ListrTaskState.FAILED;
wrapper.report(err, ListrErrorTypes.HAS_FAILED_TO_ROLLBACK);
this.close();
throw err;
}
if (this.listr.options?.exitAfterRollback !== false) {
this.close();
throw error;
}
} else {
this.state$ = ListrTaskState.FAILED;
if (this.listr.options.exitOnError !== false && await assertFunctionOrSelf(this.task?.exitOnError, context) !== false) {
wrapper.report(error, ListrErrorTypes.HAS_FAILED);
this.close();
throw error;
} else if (!this.hasSubtasks()) wrapper.report(error, ListrErrorTypes.HAS_FAILED_WITHOUT_ERROR);
}
} finally {
this.close();
}
}
close() {
this.emit(ListrTaskEventType.CLOSED);
this.listr.events.emit(ListrEventType.SHOULD_REFRESH_RENDER);
this.complete();
}
};
var ListrEventManager = class extends EventManager {};
/**
* Create a new task list with Listr.
*
* @see {@link https://listr2.kilic.dev/listr/listr.html}
*/
var Listr = class {
tasks = [];
errors = [];
ctx;
events;
path = [];
rendererClass;
rendererClassOptions;
rendererSelection;
boundSignalHandler;
concurrency;
renderer;
constructor(task, options, parentTask) {
this.task = task;
this.options = options;
this.parentTask = parentTask;
this.options = {
concurrent: false,
renderer: "default",
fallbackRenderer: "simple",
exitOnError: true,
exitAfterRollback: true,
collectErrors: false,
registerSignalListeners: true,
...this.parentTask?.options ?? {},
...options
};
if (this.options.concurrent === true) this.options.concurrent = Infinity;
else if (typeof this.options.concurrent !== "number") this.options.concurrent = 1;
this.concurrency = new Concurrency({ concurrency: this.options.concurrent });
if (parentTask) {
this.path = [...parentTask.listr.path, parentTask.title];
this.errors = parentTask.listr.errors;
}
if (this.parentTask?.listr.events instanceof ListrEventManager) this.events = this.parentTask.listr.events;
else this.events = new ListrEventManager();
/* istanbul ignore if */
if (this.options?.forceTTY || process.env[ListrEnvironmentVariables.FORCE_TTY]) {
process.stdout.isTTY = true;
process.stderr.isTTY = true;
}
/* istanbul ignore if */
if (this.options?.forceUnicode) process.env[ListrEnvironmentVariables.FORCE_UNICODE] = "1";
const renderer = getRenderer({
renderer: this.options.renderer,
rendererOptions: this.options.rendererOptions,
fallbackRenderer: this.options.fallbackRenderer,
fallbackRendererOptions: this.options.fallbackRendererOptions,
fallbackRendererCondition: this.options?.fallbackRendererCondition,
silentRendererCondition: this.options?.silentRendererCondition
});
this.rendererClass = renderer.renderer;
this.rendererClassOptions = renderer.options;
this.rendererSelection = renderer.selection;
/* istanbul ignore next */
this.add(task ?? []);
/* istanbul ignore if */
if (this.options.registerSignalListeners) {
this.boundSignalHandler = this.signalHandler.bind(this);
process.once("SIGINT", this.boundSignalHandler).setMaxListeners(0);
}
}
/**
* Whether this is the root task.
*/
isRoot() {
return !this.parentTask;
}
/**
* Whether this is a subtask of another task list.
*/
isSubtask() {
return !!this.parentTask;
}
/**
* Add tasks to current task list.
*
* @see {@link https://listr2.kilic.dev/task/task.html}
*/
add(tasks) {
this.tasks.push(...this.generate(tasks));
}
/**
* Run the task list.
*
* @see {@link https://listr2.kilic.dev/listr/listr.html#run-the-generated-task-list}
*/
async run(context) {
if (!this.renderer) this.renderer = new this.rendererClass(this.tasks, this.rendererClassOptions, this.events);
await this.renderer.render();
this.ctx = this.options?.ctx ?? context ?? {};
await Promise.all(this.tasks.map((task) => task.check(this.ctx)));
try {
await Promise.all(this.tasks.map((task) => this.concurrency.add(() => this.runTask(task))));
this.renderer.end();
this.removeSignalHandler();
} catch (err) {
if (this.options.exitOnError !== false) {
this.renderer.end(err);
this.removeSignalHandler();
throw err;
}
}
return this.ctx;
}
generate(tasks) {
tasks = Array.isArray(tasks) ? tasks : [tasks];
return tasks.map((task) => {
let rendererTaskOptions;
if (this.rendererSelection === ListrRendererSelection.PRIMARY) rendererTaskOptions = task.rendererOptions;
else if (this.rendererSelection === ListrRendererSelection.SECONDARY) rendererTaskOptions = task.fallbackRendererOptions;
return new Task(this, task, this.options, this.rendererClassOptions, rendererTaskOptions);
});
}
async runTask(task) {
if (!await task.check(this.ctx)) return;
return new TaskWrapper(task).run(this.ctx);
}
signalHandler() {
this.tasks?.forEach(async (task) => {
if (task.isPending()) task.state$ = ListrTaskState.FAILED;
});
if (this.isRoot()) {
this.renderer?.end(/* @__PURE__ */ new Error("Interrupted."));
process.exit(127);
}
}
removeSignalHandler() {
if (this.boundSignalHandler) process.removeListener("SIGINT", this.boundSignalHandler);
}
};
//#endregion
//#region src/manager.ts
/**
* Creates a new Listr2 task manager.
*
* Useful for creating a single instance of Listr2 with pre-set settings.
*
* @see {@link https://listr2.kilic.dev/listr/manager.html}
*/
var Manager = class {
errors = [];
tasks = [];
constructor(options) {
this.options = options;
}
get ctx() {
return this.options.ctx;
}
set ctx(ctx) {
this.options.ctx = ctx;
}
add(tasks, options) {
this.tasks = [...this.tasks, this.indent(tasks, options)];
}
async runAll(options) {
options = {
...this.options,
...options
};
const tasks = [...this.tasks];
this.tasks = [];
const ctx = await this.run(tasks, options);
return ctx;
}
newListr(tasks, options) {
return new Listr(tasks, options);
}
indent(tasks, options, taskOptions) {
options = {
...this.options,
...options
};
if (typeof tasks === "function") return {
...taskOptions,
task: (ctx) => this.newListr(tasks(ctx), options)
};
return {
...taskOptions,
task: () => this.newListr(tasks, options)
};
}
async run(tasks, options) {
options = {
...this.options,
...options
};
const task = this.newListr(tasks, options);
const ctx = await task.run();
this.errors.push(...task.errors);
return ctx;
}
};
//#endregion
export { Manager };