You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
5.4 KiB
174 lines
5.4 KiB
2 years ago
|
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};
|
||
|
|