Skip to content

Instantly share code, notes, and snippets.

@mini-ninja-64
Last active July 6, 2025 23:52
Show Gist options
  • Save mini-ninja-64/a37961bf1acb8b9d00f3a4a0be069417 to your computer and use it in GitHub Desktop.
Save mini-ninja-64/a37961bf1acb8b9d00f3a4a0be069417 to your computer and use it in GitHub Desktop.
JavaScript/TypeScript Proxy object helpers
const targetObject = {
message1: "hello",
message2: "everyone",
test1: function () { console.log(this); },
test2: () => { console.log(this); },
test3: () => ({ nest1: () => ({ nest2: () => "value" }) }),
test4: { foo: { bar: () => ({ a: 1 }) } },
};
const proxy1 = proxify(targetObject, {
functionExecCallback: (callChain) => {
if (callChain.matches(".test4.foo.bar.()")) {
console.log("hhehhehehe ive highjacked uuuu");
}
}
});
proxy1.test1()
console.log(proxy1.message1)
proxy1.test2()
console.log(proxy1.message2)
console.log(proxy1.test3().nest1().nest2())
console.log(proxy1.test4.foo.bar())
// Note: Will probs move to being a library at some point
// Note: Currently a WIP
export type BaseTypes = "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function";
export interface JsProxyProperties { value: any; receiver: any; target: any; }
export interface ProxifyInternal {
rawValue: any;
valueCallbackResult: any;
}
const PROXIFY_INTERNAL_KEY = "__proxify_internal"
function proxifyValue({ value, receiver, target }: JsProxyProperties, currentCaller: CallChain, options: ProxifyOptions): any {
const valueCallbackReturn = options.valueCallback(currentCaller, value);
const normalisedValue = valueCallbackReturn === undefined ? value : valueCallbackReturn.value;
const internalFields = { [PROXIFY_INTERNAL_KEY]: { rawValue: value, valueCallbackResult: valueCallbackReturn } };
if (normalisedValue instanceof Promise) {
let promise = new Promise((resolve, reject) => {
normalisedValue.then((resolvedValue) => {
resolve(proxifyValue({ value: resolvedValue, receiver, target }, currentCaller.extend("resolved"), options));
}).catch(rejectedValue => {
reject(proxifyValue({ value: rejectedValue, receiver, target }, currentCaller.extend("rejected"), options));
})
});
return Object.assign(promise, internalFields);
} else if (normalisedValue instanceof Function) {
// Do not make this an arrow function, it wont work for *this* reasons :p
const f = function (...actualArgs: any[]) {
const t = this === receiver ? target : this;
const actualFunction = (...args: any[]) => normalisedValue.apply(t, args);
currentCaller = currentCaller.extend("executed");
const functionExeccallbackReturn = options.functionExecCallback(currentCaller, actualArgs, actualFunction);
const result = functionExeccallbackReturn === undefined ? actualFunction(...actualArgs) : functionExeccallbackReturn.value;
return proxifyValue({ value: result, receiver, target }, currentCaller, options);
};
return Object.assign(f, internalFields);
} else if (normalisedValue instanceof Object) {
var p = new Proxy(normalisedValue, handler(currentCaller, options));
return Object.assign(p, internalFields);
}
return normalisedValue;
}
function handler(caller: CallChain = new CallChain([]), options: ProxifyOptions) {
return {
get(target: any, property: string, receiver: any): any {
let value = target[property];
if (property === PROXIFY_INTERNAL_KEY) return value;
let currentCaller = caller.extend({ name: property, type: typeof value });
return proxifyValue({ value, receiver, target }, currentCaller, options);
}
}
}
interface BaseCaller {
name: string
type: BaseTypes
}
export type Caller = BaseCaller | "executed" | "resolved" | "rejected"
export class CallChain {
chain: Caller[]
constructor(chain: Caller[]) {
this.chain = chain;
}
extend(caller: Caller): CallChain {
return new CallChain([...this.chain, caller]);
}
toCallChainString(): string {
return this.chain
.map(call => `.${callerString(call)}`)
.join('');
}
toString(): string {
return this.chain.toString();
}
}
function callerString(caller: Caller) {
switch (caller) {
case "executed": return "()";
case "resolved": return "__asyncResolved()";
case "rejected": return "__asyncRejected()";
default: return caller.name;
}
}
export type ProxifyReturn = { value: any } | void
export interface ProxifyOptions {
valueCallback: (caller: CallChain, rawValue: any) => ProxifyReturn,
functionExecCallback: (caller: CallChain, args: any[], func: (...args: any[]) => any) => ProxifyReturn
}
export function proxify<T extends object>(target: T, proxyifyOptions: Partial<ProxifyOptions> = {}): T {
const defaults: ProxifyOptions = {
valueCallback: () => { },
functionExecCallback: () => { }
}
const options = { ...defaults, ...proxyifyOptions }
return new Proxy(target, handler(new CallChain([]), options))
}
export function unproxy<T extends object>(proxied: T): T {
if (PROXIFY_INTERNAL_KEY in proxied) {
var internals = proxied[PROXIFY_INTERNAL_KEY] as ProxifyInternal;
return internals.rawValue
}
return proxied;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment