function monkeyPatch(name, parentObj, patches) { const injId = Symbol() const before = (patches["before"] === undefined) ? (...args)=>args : patches["before"]; const instead = (patches["instead"] === undefined) ? (res,...args)=>res(...args) : patches["instead"]; const after = (patches["after"] === undefined) ? (args, res)=>res : patches["after"]; const handler = { apply: (target, thisArg, [ctx, args]) => { before.apply(ctx, args); res = instead(target.bind(ctx), args) return after.apply(ctx, args.concat([res])); } }; const prox = new Proxy(parentObj[name], handler); const orig = parentObj[name]; parentObj[name] = function() { return prox(this, [arguments]); }; const unpatch = () => { parentObj[name] = orig; } parentObj[injId] = { name: name, orig: orig, unpatch: unpatch }; return unpatch; } function before(name, parentObj, func) { return monkeyPatch(name, parentObj, {before: func}) } function instead(name, parentObj, func) { return monkeyPatch(name, parentObj, {instead: func}) } function after(name, parentObj, func) { return monkeyPatch(name, parentObj, {after: func}) } module.exports = { monkeyPatch, before, instead, after }