GoonCord/api/patcher.ts
2025-12-10 21:43:06 +01:00

67 lines
2 KiB
TypeScript

const {
after: _after,
before: _before,
instead: _instead
} = require("spitroast");
/** @internal */
export const _patcherDelaySymbol = Symbol.for("bunny.api.patcher.delay");
type Unpatcher = () => boolean;
type DelayCallback = (callback: (target: any) => void) => unknown;
type Thenable = { then: typeof Promise.prototype.then };
interface PatchFn<Callback> {
(func: string, parent: any, callback: Callback, once?: boolean): Unpatcher;
await(func: string, parent: Promise<unknown>, callback: Callback, once?: boolean): Unpatcher;
}
type BeforeFn = PatchFn<(args: any[]) => unknown | unknown[]>;
type InsteadFn = PatchFn<(args: any[], origFunc: Function) => unknown>;
type AfterFn = PatchFn<(args: any[], ret: any) => unknown>;
function create(fn: Function) {
function patchFn(this: any, ...args: any[]) {
if (typeof args[1][_patcherDelaySymbol] === "function") {
const delayCallback: DelayCallback = args[1][_patcherDelaySymbol];
let cancel = false;
let unpatch = () => cancel = true;
delayCallback(target => {
if (cancel) return;
args[1] = target;
unpatch = fn.apply(this, args);
});
return () => unpatch();
}
return fn.apply(this, args);
}
function promisePatchFn(this: any, ...args: [any, Thenable, ...any]) {
const thenable = args[1];
if (!thenable || !("then" in thenable)) throw new Error("target is not a then-able object");
let cancel = false;
let unpatch = () => cancel = true;
thenable.then(target => {
if (cancel) return;
args[1] = target;
unpatch = patchFn.apply(this, args);
});
return () => unpatch();
}
return Object.assign(patchFn, { await: promisePatchFn });
}
export const after = create(_after) as AfterFn;
export const before = create(_before) as BeforeFn;
export const instead = create(_instead) as InsteadFn;
/** @internal */
export default { after, before, instead };