chain-simple
Version:
Main purpose of this package is - provide simple way to build chain between any item methods
174 lines • 7.26 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.chainProps = chainProps;
exports.makeConstructorInstancePropertiesChainable = makeConstructorInstancePropertiesChainable;
const sat_utils_1 = require("sat-utils");
const logger_1 = require("./logger");
logger_1.logger.setLogLevel(process.env.CHAIN_SIMPLE_LOG_LEVEL);
function extendProxed(target, propName, receiver, config) {
if ((0, sat_utils_1.isObject)(config) && (0, sat_utils_1.isFunction)(config.extendProxed) && (0, sat_utils_1.isUndefined)(Reflect.get(target, propName, receiver))) {
try {
const extension = config.extendProxed(propName);
if ((0, sat_utils_1.isObject)(extension)) {
Object.assign(target, extension);
}
else if ((0, sat_utils_1.isFunction)(extension)) {
const result = extension(target);
Object.assign(target, result);
}
}
catch (error) {
console.error(error);
}
return target;
}
}
/**
* @example
* const {chainProps} = require('chain-simple');
* const obj = {
* async method1() {
* return Promise.resolve(1).then(value => {
* console.log('method1', value);
* return value;
* });
* },
* async method2() {
* return Promise.resolve(2).then(value => {
* console.log('method2', value);
* return value;
* });
* },
* async method3() {
* return Promise.resolve(3).then(value => {
* console.log('method3', value);
* return value;
* });
* },
* };
* const chainableObj = chainProps(obj);
* obj.method1().method3().then((val) => console.log(val))
*
*
* @param {!object} item
* @param {{getEntity: string}} [config] config to describe how to get original not project object
* @returns {object} object with chainable properties
*/
function chainProps(item, config) {
const promiseCallableProps = ['then', 'catch', 'finally'];
const propsList = [];
if ((0, sat_utils_1.isObject)(config) && config.getEntityPropList) {
if (!(0, sat_utils_1.isObject)(config.getEntityPropList) && !(0, sat_utils_1.isArray)(config.getEntityPropList)) {
throw new TypeError('config "getEntityPropList" should be an array or an object');
}
propsList.push(...((0, sat_utils_1.isObject)(config.getEntityPropList)
? Object.keys(config.getEntityPropList)
: config.getEntityPropList));
}
if (!(0, sat_utils_1.canBeProxed)(item)) {
throw new TypeError('chainProps(): first argument should be an entity that can be proxed');
}
if (!(0, sat_utils_1.isUndefined)(config) && !(0, sat_utils_1.isObject)(config)) {
throw new TypeError('chainProps(): second argument should be an object');
}
const _config = { ...config };
let proxifiedResult = item;
const proxed = new Proxy(item, {
get(_t, p, r) {
if (propsList.length && propsList.includes(p)) {
const propValue = Reflect.getOwnPropertyDescriptor(item, p)?.value;
if ((0, sat_utils_1.isFunction)(propValue) || (0, sat_utils_1.isAsyncFunction)(propValue)) {
return item[p].bind(item);
}
return item[p];
}
if (_config.extendOnly) {
extendProxed(item, p, r, config);
return item[p];
}
if (_config.getEntity === p) {
return item;
}
if (p === Symbol.toStringTag) {
return proxifiedResult[Symbol.toStringTag];
}
if (p === 'toString') {
return function (...args) {
return proxifiedResult.toString(...args);
};
}
if (p === 'toJSON') {
return function () {
return proxifiedResult;
};
}
if (!promiseCallableProps.includes(p)) {
extendProxed(item, p, r, config);
}
const isCallable = (0, sat_utils_1.isFunction)(Reflect.get(item, p, r)) || (0, sat_utils_1.isAsyncFunction)(Reflect.get(item, p, r));
if (!isCallable && !(0, sat_utils_1.isPromise)(proxifiedResult) && item[p] && !proxifiedResult[p]) {
logger_1.logger.chainer(`[CHAIN_SIMPLE]: ${String(p)} is not a callable.`);
return item[p];
}
else if (isCallable) {
logger_1.logger.chainer(`[CHAIN_SIMPLE]: ${String(p)} is a callable.`);
return function (...arguments_) {
logger_1.logger.chainer(`[CHAIN_SIMPLE]: ${String(p)} is called with args: `, ...arguments);
if ((0, sat_utils_1.isPromise)(proxifiedResult)) {
logger_1.logger.chainer(`[CHAIN_SIMPLE]: previous call result is a promise`);
proxifiedResult = proxifiedResult.then(function (r) {
logger_1.logger.chainer(`[CHAIN_SIMPLE]: previous call result is: `, r);
return item[p].call(item, ...arguments_);
});
}
else {
logger_1.logger.chainer(`[CHAIN_SIMPLE]: previous call result is not a promise`);
logger_1.logger.chainer(`[CHAIN_SIMPLE]: previous call result is: `, proxifiedResult);
proxifiedResult = item[p].call(item, ...arguments_);
}
return proxed;
};
}
else if (promiseCallableProps.includes(p) && (0, sat_utils_1.isPromise)(proxifiedResult)) {
logger_1.logger.chainer(`[CHAIN_SIMPLE]: previous call result is a promise and next call is a promise method call`);
if (!(0, sat_utils_1.isPromise)(proxifiedResult)) {
return proxifiedResult;
}
return function (onRes, onRej) {
const promised = proxifiedResult;
proxifiedResult = item;
return promised[p].call(promised, onRes, onRej);
};
}
else if (proxifiedResult[p]) {
return proxifiedResult[p];
}
if (!(p in item) && p in proxifiedResult) {
return proxifiedResult[p];
}
},
/** @info base */
getPrototypeOf(_t) {
return Object.getPrototypeOf(proxifiedResult);
},
ownKeys(_t) {
return Object.getOwnPropertyNames(proxifiedResult);
},
getOwnPropertyDescriptor(_t, p) {
return Object.getOwnPropertyDescriptor(proxifiedResult, p);
},
});
return proxed;
}
function handlerConstructor(config) {
return {
construct(target, args) {
const item = new target(...args);
return chainProps(item, config);
},
};
}
function makeConstructorInstancePropertiesChainable(constructorFunction, config) {
return new Proxy(constructorFunction, handlerConstructor(config));
}
//# sourceMappingURL=index.js.map