Created
May 9, 2025 09:40
-
-
Save bigmistqke/b7904ce4fe423fdb3b82b2bb426a37c7 to your computer and use it in GitHub Desktop.
ProxyLogger
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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