@creditkarma/dynamic-config
Version:
Dynamic Config for Node.js backed by Consul and Vault
164 lines • 5.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveObjectPromises = exports.valuesForPromises = exports.race = exports.some = void 0;
const object_1 = require("./object");
function executePromiseAtIndex(index, promise) {
return promise.then((val) => {
return [index, val];
}, (err) => {
return [index, null];
});
}
function some(promises) {
return new Promise((resolve, reject) => {
const temp = [];
let count = 0;
promises.forEach((next, index) => {
executePromiseAtIndex(index, next).then((tuple) => {
count++;
temp[tuple[0]] = tuple[1];
if (count === promises.length) {
const result = temp.filter((val) => val !== null);
if (result.length > 0) {
resolve(result);
}
else {
reject(new Error('All promises completed without success'));
}
}
}, (err) => {
count++;
if (count >= promises.length) {
const result = temp.filter((val) => val !== null);
if (result.length > 0) {
resolve(result);
}
else {
reject(new Error('All promises completed without success'));
}
}
});
});
});
}
exports.some = some;
/**
* Given an array of Promises return a new Promise that resolves with the value
* of the first of the array to resolve, ignoring rejections. The resulting Promise
* only rejects if all of the Promises reject.
*/
function race(promises) {
const count = promises.length;
let current = 0;
let resolved = false;
return new Promise((resolve, reject) => {
promises.forEach((next) => {
next.then((val) => {
if (!resolved) {
resolved = true;
resolve(val);
}
}, (err) => {
current++;
if (!resolved && current === count) {
// Propagate the last promise error back to caller
reject(err);
}
});
});
});
}
exports.race = race;
/**
* Recursively traverses an object, looking for keys with Promised values and returns a Promise of
* the object with all nested Promises resolved.
*/
async function valuesForPromises(promises) {
return Promise.all(promises.map((next, index) => {
return resolveAtIndex(next, index);
})).then((values) => {
return processValues(values);
});
}
exports.valuesForPromises = valuesForPromises;
function processValues(values) {
return values
.sort((a, b) => {
if (a[1] < b[1]) {
return -1;
}
else {
return 1;
}
})
.map((next) => {
return next[0];
});
}
function resolveAtIndex(promise, index) {
return new Promise((resolve, reject) => {
promise.then((val) => {
return resolve([val, index]);
}, (err) => {
return reject(err);
});
});
}
function appendUpdateForObject(value, path, updates) {
if (value instanceof Promise) {
updates.push([path, value]);
}
else if (typeof value === 'object') {
collectUnresolvedPromises(value, path, updates);
}
}
function collectUnresolvedPromises(obj, path = [], updates = []) {
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
const value = obj[i];
const newPath = [...path, `${i}`];
appendUpdateForObject(value, newPath, updates);
}
return updates;
}
else if (obj instanceof Promise) {
updates.push([path, obj]);
return updates;
}
else if (typeof obj === 'object') {
for (const key of Object.keys(obj)) {
const value = obj[key];
const newPath = [...path, key];
appendUpdateForObject(value, newPath, updates);
}
return updates;
}
else {
return [];
}
}
async function handleUnresolved(unresolved, base) {
const paths = unresolved.map((next) => next[0].join('.'));
const promises = unresolved.map((next) => next[1]);
const resolvedPromises = await Promise.all(promises.map((next) => {
return next.then((val) => {
const nested = collectUnresolvedPromises(val);
if (nested.length > 0) {
return handleUnresolved(nested, val);
}
else {
return Promise.resolve(val);
}
});
}));
const newObj = resolvedPromises.reduce((acc, next, currentIndex) => {
return (0, object_1.setValueForKey)(paths[currentIndex], next, acc);
}, base);
return newObj;
}
async function resolveObjectPromises(obj) {
const unresolved = collectUnresolvedPromises(obj);
return handleUnresolved(unresolved, obj);
}
exports.resolveObjectPromises = resolveObjectPromises;
//# sourceMappingURL=promises.js.map