intercept.js

/**
 *  @typedef {Function} before
 *  @param fn {function}
 *  @param b4fn  {function}
 *  @param ctx {object}  Info on the page you want to request

 */

export function before(fn, b4fn, ctx) {
    return function () {
        let args  = arguments;
        let ths   = ctx || this;
        let b4val = b4fn.apply(ths, args);
        return b4val?.then ? b4val.then((val) => fn.apply(ths, args)) : fn.apply(ths, args);
    };
}

Function.prototype.before = function (b4fn, ctx) {
    return before(this, b4fn, ctx);
};

export function unshift(fn, b4fn, ctx) {
    return function () {
        let ths   = ctx || this;
        let b4val = b4fn.apply(ths, arguments);
        return b4val?.then ? b4val.then((val) => fn.apply(ths, [val])) : fn.apply(ths, [b4val]);
    };
}

Function.prototype.unshift = function (b4fn, ctx) {
    return unshift(this, b4fn, ctx);
};

/**
 *  @typedef after {Function}
 *  @param fn {function}
 *  @param afn {function}  the function you want to execute after, it will get 2 arguments, the first is the return value of fn, the second is the arguments of fn
 *  @param [ctx] {object}
 */
export function after(fn, afn, ctx) {
    /**
     * @param afn   {function}
     */
    return function () {
        let ths    = ctx || this;
        let val    = fn.apply(ths, arguments);
        let retAfn = val?.then ? val.then((val) => afn.apply(ths, arguments)) : afn.apply(ths, arguments);
        return retAfn?.then ? retAfn.then(() => val) : val;
    };
}

Function.prototype.after = function (afn, ctx) {
    return after(this, afn, ctx);
};

export function pipe(fn, afn, ctx) {
    let ths = ctx || this;
    return function (){
        return afn.apply(ths, [fn.apply(ths, arguments)]);
    }

}

Function.prototype.pipe = function (afn, ctx) {
    return pipe(this, afn, ctx);
};

/**
 * Wraps a function with before and after functions.
 *
 * @param {Function} fn - The function to wrap.
 * @param {Function} b4fn - The function to execute before the wrapped function.
 * @param {Function} afn - The function to execute after the wrapped function.
 * @param {Object} [ctx] - The context to use when calling the functions.
 * @returns {Function} - The wrapped function.
 */
export function around(fn, b4fn, afn, ctx) {
    return function () {
        let ths    = ctx || this;
        let b4val  = b4fn.apply(ths, arguments);
        let ret    = b4val?.then ? b4val.then(() => fn.apply(ths, arguments)) : fn.apply(ths, arguments);
        let aftRet = ret?.then ? ret.then((val) => afn.apply(ths, arguments)) : afn.apply(ths, arguments);
        return aftRet?.then ? aftRet.then(() => ret) : ret;
    };
}

Function.prototype.around = function (b4fn, afn, ctx) {
    return around(this, b4fn, afn, ctx);
};

/**
 * wrap a function with before and after functions,
 * function b4fn will be executed before the function,
 * the return value of before function will be passed to the function,
 * function afn will be executed after the function fn;
 * @param fn
 * @param b4fn
 * @param afn
 * @param ctx
 * @return {function(): *}
 */
export function wrapper(fn, b4fn, afn, ctx) {
    afn ||= (val) => val;
    return function () {
        let ths   = ctx || this;
        let b4ret = b4fn?.apply(ths, arguments);
        let _fn   = b4ret => {
            let ret = fn.apply(ths, [b4ret]);
            return ret?.then ? ret.then(afn) : afn(b4ret);
        };
        return b4ret?.then ? b4ret.then(_fn) : _fn(b4ret);
    };
}


// Add a wrapper method to the Function prototype that wraps a function with before and after functions
Function.prototype.wrapper = function (b4fn, afn, ctx) {
    return wrapper(this, b4fn, afn, ctx);
};


// let t=co(function(){
//   console.log('co1',Date.now());
// },1,1000);
// let arr=new Array(6).fill(1);
// arr.forEach((v)=>{
//   return t(v);
// });
// setTimeout(()=>{
//     t.clear();
// },3000);
// function testFn(arg1, arg2) {
//     console.log('tfn', arg1, arg2);
//     return 20;
// }

// let fn = testFn.wrapper((arg1, arg2) => {
//     console.log('filter', arg1, arg2);
//     return [arg1 + 2, arg2 + 2];
// }, function (arg1, arg2) {
//     return arg2;
// })

// let ret = fn(1, 2);
// console.log('ret', ret);