// This file has functions / classes that allow to watch writes to the original unproxied objects (or arrays/sets/maps) // unproxied=not part of a proxy facade. Technically this can install Proxys as the prototype, to catch writes. import {ObjectProxyHandler} from "./objectChangeTracking"; import {getTrackingConfigFor} from "./class-trackers/index"; import {changeTrackedOrigObjects, isProxyForAFacade} from "./proxyFacade"; import {throwError} from "./Util"; /** * * @param obj */ export function installChangeTracker(obj: object) { !isProxyForAFacade(obj) || throwError("Cannot install change tracker on a proxy. The proxy should already support change tracking."); if(changeTrackedOrigObjects.hasObj(obj)) { return; } function inner() { const trackingConfig = getTrackingConfigFor(obj); if (trackingConfig) { if (trackingConfig.trackSettingObjectProperties) { // Achieve this with the ObjectProxyhandler. It will consider getTrackingConfigFor(obj).changeTracker itsself: const proxy = new ObjectProxyHandler(obj, trackingConfig).proxy; Object.setPrototypeOf(obj, proxy); return; } else if (trackingConfig.changeTracker !== undefined) { Object.setPrototypeOf(obj, trackingConfig.changeTracker.prototype); } else { throwError("Unhandled"); } } else { // Non-special object ? const proxy = new ObjectProxyHandler(obj, trackingConfig).proxy; Object.setPrototypeOf(obj, proxy); } } inner(); changeTrackedOrigObjects._register(obj); } export function unInstallChangeTracker(obj: object) { if(!changeTrackedOrigObjects.hasObj(obj)) { // Already uninstalled? return false } const origPrototype = Object.getPrototypeOf(Object.getPrototypeOf(obj)); Object.setPrototypeOf(obj, origPrototype); // Remove proto from the chain changeTrackedOrigObjects._unregister(obj); return true; } /** * Temporarily uninstalls them, if an (ancestor) caller has allowed it * @param obj */ export function performance_temporarilyUninstallChangeTrackersIfAllowed(obj: object) { if(performance_temporarilyUninstallObjects !== undefined) { const uninstalled = unInstallChangeTracker(obj); if(uninstalled) { performance_temporarilyUninstallObjects?.push(obj); } } } /** * Performance: When set, */ let performance_temporarilyUninstallObjects: object[] | undefined = undefined /** * Uninstalls the changes trackers temporarily while running the specified function. This can improve performance if you have many ready on few objects * @param execFn */ export function performance_withoutChangeEvents(execFn: () => T) { if(performance_temporarilyUninstallObjects !== undefined) { // Already wrapped at parent level? return execFn() } performance_temporarilyUninstallObjects = []; try { return execFn(); } finally { // Re-install change trackers: for(const obj of performance_temporarilyUninstallObjects) { installChangeTracker(obj) } performance_temporarilyUninstallObjects = undefined; } }