const npm = { u: require('util'), os: require('os'), utils: require('../utils/static') }; /** * @class errors.BatchError * @augments external:Error * @description * This type represents all errors rejected by method {@link batch}, except for {@link external:TypeError TypeError} * when the method receives invalid input parameters. * * @property {string} name * Standard {@link external:Error Error} property - error type name = `BatchError`. * * @property {string} message * Standard {@link external:Error Error} property - the error message. * * It represents the message of the first error encountered in the batch, and is a safe * version of using `first.message`. * * @property {string} stack * Standard {@link external:Error Error} property - the stack trace. * * @property {array} data * Array of objects `{success, result, [origin]}`: * - `success` = true/false, indicates whether the corresponding value in the input array was resolved. * - `result` = resolved data, if `success`=`true`, or else the rejection reason. * - `origin` - set only when failed as a result of an unsuccessful call into the notification callback * (parameter `cb` of method {@link batch}) * * The array has the same size as the input one that was passed into method {@link batch}, providing direct mapping. * * @property {} stat * Resolution Statistics. * * @property {number} stat.total * Total number of elements in the batch. * * @property {number} stat.succeeded * Number of resolved values in the batch. * * @property {number} stat.failed * Number of rejected values in the batch. * * @property {number} stat.duration * Time in milliseconds it took to settle all values. * * @property {} first * The very first error within the batch, with support for nested batch results, it is also the same error * as $[promise.all] would provide. * * @see {@link batch} * */ class BatchError extends Error { constructor(result, errors, duration) { function getErrors() { const err = new Array(errors.length); for (let i = 0; i < errors.length; i++) { err[i] = result[errors[i]].result; if (err[i] instanceof BatchError) { err[i] = err[i].getErrors(); } } npm.utils.extend(err, '$isErrorList', true); return err; } const e = getErrors(); let first = e[0]; while (first && first.$isErrorList) { first = first[0]; } let message; if (first instanceof Error) { message = first.message; } else { if (typeof first !== 'string') { first = npm.u.inspect(first); } message = first; } super(message); this.name = this.constructor.name; this.data = result; // we do not show it within the inspect, because when the error // happens for a nested result, the output becomes a mess. this.first = first; this.stat = { total: result.length, succeeded: result.length - e.length, failed: e.length, duration: duration }; this.getErrors = getErrors; Error.captureStackTrace(this, this.constructor); } /** * @method errors.BatchError.getErrors * @description * Returns the complete list of errors only. * * It supports nested batch results, presented as a sub-array. * * @returns {array} */ } /** * @method errors.BatchError.toString * @description * Creates a well-formatted multi-line string that represents the error. * * It is called automatically when writing the object into the console. * * The output is an abbreviated version of the error, because the complete error * is often too much for displaying or even logging, as a batch can be of any size. * Therefore, only errors are rendered from the `data` property, alongside their indexes, * and only up to the first 5, to avoid polluting the screen or the log file. * * @param {number} [level=0] * Nested output level, to provide visual offset. * * @returns {string} */ BatchError.prototype.toString = function (level) { level = level > 0 ? parseInt(level) : 0; const gap0 = npm.utils.messageGap(level), gap1 = npm.utils.messageGap(level + 1), gap2 = npm.utils.messageGap(level + 2), lines = [ 'BatchError {', gap1 + 'stat: { total: ' + this.stat.total + ', succeeded: ' + this.stat.succeeded + ', failed: ' + this.stat.failed + ', duration: ' + this.stat.duration + ' }', gap1 + 'errors: [' ]; // In order to avoid polluting the error log or the console, // we limit the log output to the top 5 errors: const maxErrors = 5; let counter = 0; this.data.forEach((d, index) => { if (!d.success && counter < maxErrors) { lines.push(gap2 + index + ': ' + npm.utils.formatError(d.result, level + 2)); counter++; } }); lines.push(gap1 + ']'); lines.push(gap0 + '}'); return lines.join(npm.os.EOL); }; npm.utils.addInspection(BatchError, function () { return this.toString(); }); module.exports = {BatchError};