Skip to content

Instantly share code, notes, and snippets.

@bigmistqke
Created May 9, 2025 09:40
Show Gist options
  • Save bigmistqke/b7904ce4fe423fdb3b82b2bb426a37c7 to your computer and use it in GitHub Desktop.
Save bigmistqke/b7904ce4fe423fdb3b82b2bb426a37c7 to your computer and use it in GitHub Desktop.
ProxyLogger
/* slopped together with bolt.new */
// Type definition for the proxy handler
type DeepProxyHandler<T extends object> = ProxyHandler<T>;
// Function to check if a value is an object that can be proxied
const canBeProxied = (value: any): boolean => {
return value !== null && typeof value === 'object' && !isProxyIgnored(value);
};
// Some objects should not be proxied (like Date, etc.)
const isProxyIgnored = (value: any): boolean => {
return (
value instanceof Date ||
value instanceof RegExp ||
value instanceof Error ||
value instanceof WeakMap ||
value instanceof WeakSet ||
value instanceof Promise
);
};
/**
* Creates a deeply logged proxy that recursively proxies all nested objects and arrays
* @param target The object or array to proxy
* @param label A label to identify this proxy in logs
* @param path The current property path (used internally for nested objects)
* @param seen A WeakMap to track already proxied objects (prevents infinite recursion)
* @returns A proxied version of the target
*/
export function createDeepLoggerProxy<T extends object>(
target: T,
label = 'proxy',
path = '',
seen = new WeakMap<object, any>()
): T {
// Validate that target is an object
if (target === null || typeof target !== 'object') {
throw new Error('Cannot create proxy with a non-object as target');
}
// Prevent infinite recursion by tracking objects we've already proxied
if (seen.has(target)) {
return seen.get(target);
}
// Create the proxy handler with all available traps
const handler: DeepProxyHandler<T> = {
get(target: T, prop: string | symbol, receiver: any): any {
const propStr = String(prop);
const currentPath = path ? `${path}.${propStr}` : propStr;
// Special handling for Symbol.iterator
if (prop === Symbol.iterator && Array.isArray(target)) {
console.log(`[${label}] get Symbol.iterator`);
return function* () {
const array = target as unknown as any[];
for (let i = 0; i < array.length; i++) {
const value = array[i];
yield canBeProxied(value)
? createDeepLoggerProxy(value, label, `${currentPath}[${i}]`, seen)
: value;
}
};
}
console.log(`[${label}] get`, currentPath);
// Get the original value
const value = Reflect.get(target, prop, receiver);
// If the value is a method, bind it to the target
if (typeof value === 'function' &&
(Array.isArray(target) || Object.prototype.hasOwnProperty.call(target, prop))) {
// For methods like Array.prototype.map, filter, etc.
return function(...args: any[]) {
console.log(`[${label}] call method`, currentPath, args);
const result = value.apply(this === receiver ? target : this, args);
// If the result is an object that can be proxied, proxy it
if (canBeProxied(result)) {
return createDeepLoggerProxy(
result,
label,
`${currentPath}()`,
seen
);
}
return result;
};
}
// If the value is an object, recursively proxy it
if (canBeProxied(value)) {
return createDeepLoggerProxy(
value,
label,
currentPath,
seen
);
}
return value;
},
set(target: T, prop: string | symbol, value: any, receiver: any): boolean {
const propStr = String(prop);
const currentPath = path ? `${path}.${propStr}` : propStr;
console.log(`[${label}] set`, currentPath, '=', value);
return Reflect.set(target, prop, value, receiver);
},
has(target: T, prop: string | symbol): boolean {
const propStr = String(prop);
const currentPath = path ? `${path}.${propStr}` : propStr;
console.log(`[${label}] has`, currentPath);
return Reflect.has(target, prop);
},
ownKeys(target: T): ArrayLike<string | symbol> {
const currentPath = path || 'root';
console.log(`[${label}] ownKeys`, currentPath);
return Reflect.ownKeys(target);
},
getOwnPropertyDescriptor(target: T, prop: string | symbol): PropertyDescriptor | undefined {
const propStr = String(prop);
const currentPath = path ? `${path}.${propStr}` : propStr;
console.log(`[${label}] getOwnPropertyDescriptor`, currentPath);
return Reflect.getOwnPropertyDescriptor(target, prop);
},
defineProperty(target: T, prop: string | symbol, descriptor: PropertyDescriptor): boolean {
const propStr = String(prop);
const currentPath = path ? `${path}.${propStr}` : propStr;
console.log(`[${label}] defineProperty`, currentPath, descriptor);
return Reflect.defineProperty(target, prop, descriptor);
},
deleteProperty(target: T, prop: string | symbol): boolean {
const propStr = String(prop);
const currentPath = path ? `${path}.${propStr}` : propStr;
console.log(`[${label}] deleteProperty`, currentPath);
return Reflect.deleteProperty(target, prop);
},
getPrototypeOf(target: T): object | null {
const currentPath = path || 'root';
console.log(`[${label}] getPrototypeOf`, currentPath);
return Reflect.getPrototypeOf(target);
},
setPrototypeOf(target: T, prototype: object | null): boolean {
const currentPath = path || 'root';
console.log(`[${label}] setPrototypeOf`, currentPath);
return Reflect.setPrototypeOf(target, prototype);
},
isExtensible(target: T): boolean {
const currentPath = path || 'root';
console.log(`[${label}] isExtensible`, currentPath);
return Reflect.isExtensible(target);
},
preventExtensions(target: T): boolean {
const currentPath = path || 'root';
console.log(`[${label}] preventExtensions`, currentPath);
return Reflect.preventExtensions(target);
},
apply(target: any, thisArg: any, args: any[]): any {
const currentPath = path || 'function';
console.log(`[${label}] apply`, currentPath, args);
const result = Reflect.apply(target, thisArg, args);
// If the result is an object that can be proxied, proxy it
if (canBeProxied(result)) {
return createDeepLoggerProxy(
result,
label,
`${currentPath}()`,
seen
);
}
return result;
},
construct(target: any, args: any[], newTarget: Function): object {
const currentPath = path || 'constructor';
console.log(`[${label}] construct`, currentPath, args);
const result = Reflect.construct(target, args, newTarget);
// If the result is an object that can be proxied, proxy it
if (canBeProxied(result)) {
return createDeepLoggerProxy(
result,
label,
`${currentPath}(new)`,
seen
);
}
return result;
}
};
// Create the proxy
const proxy = new Proxy(target, handler);
// Store the proxy in the seen map to prevent infinite recursion
seen.set(target, proxy);
return proxy;
}
// Helper function to clear the console
export function clearLogs(): void {
console.clear();
}
// Export the original function for comparison
export function createLoggedProxy<T extends object>(target: T, label = 'proxy'): T {
if (target === null || typeof target !== 'object') {
throw new Error('Cannot create proxy with a non-object as target');
}
const handler: ProxyHandler<T> = {
get(target, prop, receiver) {
console.log(`[${label}] get`, String(prop));
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`[${label}] set`, String(prop), '=', value);
return Reflect.set(target, prop, value, receiver);
},
has(target, prop) {
console.log(`[${label}] has`, String(prop));
return Reflect.has(target, prop);
},
ownKeys(target) {
console.log(`[${label}] ownKeys`);
return Reflect.ownKeys(target);
},
getOwnPropertyDescriptor(target, prop) {
console.log(`[${label}] getOwnPropertyDescriptor`, String(prop));
return Reflect.getOwnPropertyDescriptor(target, prop);
},
defineProperty(target, prop, descriptor) {
console.log(`[${label}] defineProperty`, String(prop), descriptor);
return Reflect.defineProperty(target, prop, descriptor);
},
deleteProperty(target, prop) {
console.log(`[${label}] deleteProperty`, String(prop));
return Reflect.deleteProperty(target, prop);
},
getPrototypeOf(target) {
console.log(`[${label}] getPrototypeOf`);
return Reflect.getPrototypeOf(target);
},
setPrototypeOf(target, prototype) {
console.log(`[${label}] setPrototypeOf`);
return Reflect.setPrototypeOf(target, prototype);
},
isExtensible(target) {
console.log(`[${label}] isExtensible`);
return Reflect.isExtensible(target);
},
preventExtensions(target) {
console.log(`[${label}] preventExtensions`);
return Reflect.preventExtensions(target);
},
apply(target: any, thisArg: any, args: any[]) {
console.log(`[${label}] apply`, args);
return Reflect.apply(target, thisArg, args);
},
construct(target: any, args: any[], newTarget: any) {
console.log(`[${label}] construct`, args);
return Reflect.construct(target, args, newTarget);
}
};
return new Proxy(target, handler);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment