CommandJam

patch.js

Aug 16th, 2024
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * @typedef {Object} PatchOptions
  3.  * @property {boolean} [block] - If true, prevents the original function from being called.
  4.  * @property {any} [value] - The value to return instead of the original function result.
  5.  */
  6.  
  7. /**
  8.  * @callback PatchCallback
  9.  * @param {Array<any>} args - The arguments passed to the function.
  10.  * @param {Function} original - The original function being patched.
  11.  * @returns {PatchOptions}
  12.  */
  13.  
  14. /**
  15.  * @typedef {Object} ReturnedPatch
  16.  * @property {function} original - The original function before patching.
  17.  * @property {function} restore - Restores the original function.
  18.  */
  19.  
  20. /**
  21.  * Monkey-patches a function with a Proxy in a given context.
  22.  *
  23.  * @param {string} name - The name of the function.
  24.  * @param {boolean} isConstructor - Whether the target is a constructor.
  25.  * @param {PatchCallback} callback
  26.  * @param {Object} [context=window] - The context in which the function exists.
  27.  * @returns {ReturnedPatch}
  28.  * @example
  29.  * const patch = patchFunction('open', false, (args, original) => {
  30.  *  console.log(`XMLHttpRequest.open called with URL: ${args[1]}`);
  31.  *  return { block: true };
  32.  * }, XMLHttpRequest.prototype);
  33.  */
  34. export function patchFunction(name, isConstructor, callback, context = window) {
  35.     if (!context || typeof context[name] !== 'function') {
  36.         throw new Error(
  37.             `Function or constructor ${name} not found in the specified context.`
  38.         );
  39.     }
  40.  
  41.     const original = context[name];
  42.  
  43.     context[name] = new Proxy(original, {
  44.         [isConstructor ? 'construct' : 'apply'](target, thisArg, argList) {
  45.             const options = callback?.(argList, original);
  46.             let result;
  47.  
  48.             if (!options?.block) {
  49.                 result = isConstructor
  50.                     ? Reflect.construct(target, argList)
  51.                     : Reflect.apply(target, thisArg, argList);
  52.             }
  53.  
  54.             return options && 'value' in options ? options.value : result;
  55.         }
  56.     });
  57.  
  58.     return {
  59.         original,
  60.         restore() {
  61.             context[name] = original;
  62.         }
  63.     };
  64. }
  65.  
Advertisement
Add Comment
Please, Sign In to add comment