diff --git a/packages/internal/src/utils.js b/packages/internal/src/utils.js index 96b18ceb1fc8..8a57428604f2 100644 --- a/packages/internal/src/utils.js +++ b/packages/internal/src/utils.js @@ -223,3 +223,59 @@ export const makeMeasureSeconds = currentTimeMillisec => { }; return measureSeconds; }; + +/** + * @param {Error[]} errors + * @param {string} [message] + */ +const makeAggregateError = (errors, message) => { + const err = new Error(message); + Object.defineProperties(err, { + name: { + value: 'AggregateError', + }, + errors: { + value: errors, + }, + }); + return err; +}; + +/** + * @template T + * @param {readonly (T | PromiseLike)[]} values + * @returns {Promise} + */ +export const PromiseAllOrErrors = async values => { + return Promise.allSettled(values).then(results => { + const errors = /** @type {PromiseRejectedResult[]} */ ( + results.filter(({ status }) => status === 'rejected') + ).map(result => result.reason); + if (!errors.length) { + return /** @type {PromiseFulfilledResult[]} */ (results).map( + result => result.value, + ); + } else if (errors.length === 1) { + throw errors[0]; + } else { + throw makeAggregateError(errors); + } + }); +}; + +/** + * @type {( + * trier: () => Promise, + * finalizer: (error?: unknown) => Promise, + * ) => Promise} + */ export const aggregateTryFinally = async (trier, finalizer) => + trier().then( + async result => finalizer().then(() => result), + async tryError => + finalizer(tryError) + .then( + () => tryError, + finalizeError => makeAggregateError([tryError, finalizeError]), + ) + .then(error => Promise.reject(error)), + );