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};