/** * Creates a usage tracker that wraps an object with Proxies to track property access and function calls. * Returns a tuple of [trackedObject, getUsageSnapshot] where getUsageSnapshot() returns * a deep copy of the usage record including property access counts and function call arguments. */ export function usageTracker(obj) { const usage = {}; const calls = {}; function track(path) { let current = usage; for (let i = 0; i < path.length; i++) { const key = path[i]; const isLast = i === path.length - 1; if (isLast) { // For the last key, increment the counter const currentValue = current[key]; if (typeof currentValue === 'object' && currentValue !== null && !Array.isArray(currentValue)) { // If it's already an object with nested properties, keep it and add a counter const objValue = currentValue; current[key] = objValue.__count !== undefined ? { ...objValue, __count: objValue.__count + 1 } : currentValue; } else { current[key] = (currentValue || 0) + 1; } } else { // For intermediate keys, ensure they're objects const currentValue = current[key]; if (typeof currentValue !== 'object' || currentValue === null || Array.isArray(currentValue)) { current[key] = {}; } current = current[key]; } } } function trackCall(path, args) { let current = calls; for (let i = 0; i < path.length - 1; i++) { const key = path[i]; if (!current[key] || Array.isArray(current[key])) { current[key] = {}; } current = current[key]; } const lastKey = path[path.length - 1]; if (!Array.isArray(current[lastKey])) { current[lastKey] = []; } // Deep clone args to preserve undefined values ; current[lastKey].push({ args: args.map(arg => arg) }); } function createTrackedObject(target, path = []) { return new Proxy(target, { get(obj, prop) { const currentPath = [...path, prop]; const value = obj[prop]; // Track property access track(currentPath); // If it's a function, wrap it to track calls if (typeof value === 'function') { return new Proxy(value, { apply(target, thisArg, args) { trackCall(currentPath, args); return Reflect.apply(target, thisArg, args); }, }); } // If value is an object (but not null or array), wrap it in a proxy too if (value && typeof value === 'object' && !Array.isArray(value)) { return createTrackedObject(value, currentPath); } return value; }, }); } function getUsageSnapshot() { return { usage: JSON.parse(JSON.stringify(usage)), calls: JSON.parse(JSON.stringify(calls)), }; } const tracked = createTrackedObject(obj); return [tracked, getUsageSnapshot]; } //# sourceMappingURL=usageTracker.js.map