proxy-facades 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/.name +1 -0
- package/.idea/modules.xml +8 -0
- package/.idea/proxy-facades.iml +8 -0
- package/.idea/vcs.xml +6 -0
- package/DEVELOPMENT.md +15 -0
- package/LICENSE +21 -0
- package/RecordedReadOnProxiedObjectExt.d.ts +22 -0
- package/RecordedReadOnProxiedObjectExt.d.ts.map +1 -0
- package/RecordedReadOnProxiedObjectExt.js +41 -0
- package/RecordedReadOnProxiedObjectExt.js.map +1 -0
- package/RecordedReadOnProxiedObjectExt.ts +41 -0
- package/Util.d.ts +85 -0
- package/Util.d.ts.map +1 -0
- package/Util.js +239 -0
- package/Util.js.map +1 -0
- package/Util.ts +254 -0
- package/class-trackers/Array.d.ts +93 -0
- package/class-trackers/Array.d.ts.map +1 -0
- package/class-trackers/Array.js +193 -0
- package/class-trackers/Array.js.map +1 -0
- package/class-trackers/Array.ts +245 -0
- package/class-trackers/Iterator.d.ts +38 -0
- package/class-trackers/Iterator.d.ts.map +1 -0
- package/class-trackers/Iterator.js +69 -0
- package/class-trackers/Iterator.js.map +1 -0
- package/class-trackers/Iterator.ts +73 -0
- package/class-trackers/Map.d.ts +128 -0
- package/class-trackers/Map.d.ts.map +1 -0
- package/class-trackers/Map.js +310 -0
- package/class-trackers/Map.js.map +1 -0
- package/class-trackers/Map.ts +403 -0
- package/class-trackers/Set.d.ts +85 -0
- package/class-trackers/Set.d.ts.map +1 -0
- package/class-trackers/Set.js +197 -0
- package/class-trackers/Set.js.map +1 -0
- package/class-trackers/Set.ts +245 -0
- package/class-trackers/index.d.ts +7 -0
- package/class-trackers/index.d.ts.map +1 -0
- package/class-trackers/index.js +36 -0
- package/class-trackers/index.js.map +1 -0
- package/class-trackers/index.ts +38 -0
- package/class-trackers/readme.md +2 -0
- package/common.d.ts +235 -0
- package/common.d.ts.map +1 -0
- package/common.js +378 -0
- package/common.js.map +1 -0
- package/common.ts +501 -0
- package/dev_generateEsRuntimeBehaviourCheckerCode.d.ts +10 -0
- package/dev_generateEsRuntimeBehaviourCheckerCode.d.ts.map +1 -0
- package/dev_generateEsRuntimeBehaviourCheckerCode.js +76 -0
- package/dev_generateEsRuntimeBehaviourCheckerCode.js.map +1 -0
- package/dev_generateEsRuntimeBehaviourCheckerCode.ts +85 -0
- package/dist/mjs/RecordedReadOnProxiedObjectExt.d.ts +22 -0
- package/dist/mjs/RecordedReadOnProxiedObjectExt.d.ts.map +1 -0
- package/dist/mjs/RecordedReadOnProxiedObjectExt.js +37 -0
- package/dist/mjs/RecordedReadOnProxiedObjectExt.js.map +1 -0
- package/dist/mjs/Util.d.ts +85 -0
- package/dist/mjs/Util.d.ts.map +1 -0
- package/dist/mjs/Util.js +223 -0
- package/dist/mjs/Util.js.map +1 -0
- package/dist/mjs/class-trackers/Array.d.ts +93 -0
- package/dist/mjs/class-trackers/Array.d.ts.map +1 -0
- package/dist/mjs/class-trackers/Array.js +186 -0
- package/dist/mjs/class-trackers/Array.js.map +1 -0
- package/dist/mjs/class-trackers/Iterator.d.ts +38 -0
- package/dist/mjs/class-trackers/Iterator.d.ts.map +1 -0
- package/dist/mjs/class-trackers/Iterator.js +65 -0
- package/dist/mjs/class-trackers/Iterator.js.map +1 -0
- package/dist/mjs/class-trackers/Map.d.ts +128 -0
- package/dist/mjs/class-trackers/Map.d.ts.map +1 -0
- package/dist/mjs/class-trackers/Map.js +299 -0
- package/dist/mjs/class-trackers/Map.js.map +1 -0
- package/dist/mjs/class-trackers/Set.d.ts +85 -0
- package/dist/mjs/class-trackers/Set.d.ts.map +1 -0
- package/dist/mjs/class-trackers/Set.js +189 -0
- package/dist/mjs/class-trackers/Set.js.map +1 -0
- package/dist/mjs/class-trackers/index.d.ts +7 -0
- package/dist/mjs/class-trackers/index.d.ts.map +1 -0
- package/dist/mjs/class-trackers/index.js +32 -0
- package/dist/mjs/class-trackers/index.js.map +1 -0
- package/dist/mjs/common.d.ts +235 -0
- package/dist/mjs/common.d.ts.map +1 -0
- package/dist/mjs/common.js +361 -0
- package/dist/mjs/common.js.map +1 -0
- package/dist/mjs/dev_generateEsRuntimeBehaviourCheckerCode.d.ts +10 -0
- package/dist/mjs/dev_generateEsRuntimeBehaviourCheckerCode.d.ts.map +1 -0
- package/dist/mjs/dev_generateEsRuntimeBehaviourCheckerCode.js +76 -0
- package/dist/mjs/dev_generateEsRuntimeBehaviourCheckerCode.js.map +1 -0
- package/dist/mjs/index.d.ts +8 -0
- package/dist/mjs/index.d.ts.map +1 -0
- package/dist/mjs/index.js +8 -0
- package/dist/mjs/index.js.map +1 -0
- package/dist/mjs/objectChangeTracking.d.ts +43 -0
- package/dist/mjs/objectChangeTracking.d.ts.map +1 -0
- package/dist/mjs/objectChangeTracking.js +209 -0
- package/dist/mjs/objectChangeTracking.js.map +1 -0
- package/dist/mjs/origChangeTracking.d.ts +14 -0
- package/dist/mjs/origChangeTracking.d.ts.map +1 -0
- package/dist/mjs/origChangeTracking.js +58 -0
- package/dist/mjs/origChangeTracking.js.map +1 -0
- package/dist/mjs/proxyFacade.d.ts +45 -0
- package/dist/mjs/proxyFacade.d.ts.map +1 -0
- package/dist/mjs/proxyFacade.js +179 -0
- package/dist/mjs/proxyFacade.js.map +1 -0
- package/dist/mjs/watchedProxyFacade.d.ts +84 -0
- package/dist/mjs/watchedProxyFacade.d.ts.map +1 -0
- package/dist/mjs/watchedProxyFacade.js +300 -0
- package/dist/mjs/watchedProxyFacade.js.map +1 -0
- package/index.d.ts +8 -0
- package/index.d.ts.map +1 -0
- package/index.js +36 -0
- package/index.js.map +1 -0
- package/index.ts +7 -0
- package/index_esm.mjs +44 -0
- package/objectChangeTracking.d.ts +43 -0
- package/objectChangeTracking.d.ts.map +1 -0
- package/objectChangeTracking.js +214 -0
- package/objectChangeTracking.js.map +1 -0
- package/objectChangeTracking.ts +251 -0
- package/origChangeTracking.d.ts +14 -0
- package/origChangeTracking.d.ts.map +1 -0
- package/origChangeTracking.js +63 -0
- package/origChangeTracking.js.map +1 -0
- package/origChangeTracking.ts +72 -0
- package/package.json +52 -0
- package/proxyFacade.d.ts +45 -0
- package/proxyFacade.d.ts.map +1 -0
- package/proxyFacade.js +187 -0
- package/proxyFacade.js.map +1 -0
- package/proxyFacade.ts +222 -0
- package/readme.md +111 -0
- package/watchedProxyFacade.d.ts +84 -0
- package/watchedProxyFacade.d.ts.map +1 -0
- package/watchedProxyFacade.js +312 -0
- package/watchedProxyFacade.js.map +1 -0
- package/watchedProxyFacade.ts +369 -0
package/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export {RecordedRead, RecordedReadOnProxiedObject, ClassTrackingConfiguration, dualUseTracker_callOrigMethodOnTarget, IteratorReturningProxiedValue, GetIteratorValueProxiedFn, DualUseTracker, ForWatchedProxyHandler} from "./common"
|
|
2
|
+
export {ProxyFacade, FacadeProxyHandler, invalidateObject, getGlobalOrig} from "./proxyFacade";
|
|
3
|
+
export {deleteProperty, changeTrackedOrigObjects, installChangeTracker} from "./origChangeTracking"
|
|
4
|
+
export {RecordedArrayValuesRead} from "./class-trackers/Array"
|
|
5
|
+
export {RecordedMap_has, RecordedMap_get, RecordedMapEntriesRead, RecordedMapKeysRead, RecordedMapValuesRead} from "./class-trackers/Map"
|
|
6
|
+
export {RecordedSet_has, RecordedSetValuesRead} from "./class-trackers/Set"
|
|
7
|
+
export {RecordedValueRead, RecordedUnspecificRead, RecordedPropertyRead,RecordedOwnKeysRead, WatchedProxyFacade, WatchedProxyHandler} from "./watchedProxyFacade";
|
package/index_esm.mjs
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// A wrapper file for ESM to avoid the 'dual package hazard'. See https://nodejs.org/api/packages.html#approach-1-use-an-es-module-wrapper
|
|
2
|
+
|
|
3
|
+
import cjsCommon from "./common.js"
|
|
4
|
+
export const RecordedRead = cjsCommon.RecordedRead;
|
|
5
|
+
export const RecordedReadOnProxiedObject = cjsCommon.RecordedReadOnProxiedObject;
|
|
6
|
+
export const ClassTrackingConfiguration = cjsCommon.ClassTrackingConfiguration;
|
|
7
|
+
export const dualUseTracker_callOrigMethodOnTarget = cjsCommon.dualUseTracker_callOrigMethodOnTarget;
|
|
8
|
+
export const IteratorReturningProxiedValue = cjsCommon.IteratorReturningProxiedValue;
|
|
9
|
+
export const GetIteratorValueProxiedFn = cjsCommon.GetIteratorValueProxiedFn;
|
|
10
|
+
export const DualUseTracker = cjsCommon.DualUseTracker;
|
|
11
|
+
export const ForWatchedProxyHandler = cjsCommon.ForWatchedProxyHandler
|
|
12
|
+
|
|
13
|
+
import cjsProxyFacade from "./proxyFacade"
|
|
14
|
+
export const ProxyFacade = cjsProxyFacade.ProxyFacade;
|
|
15
|
+
export const FacadeProxyHandler = cjsProxyFacade.FacadeProxyHandler;
|
|
16
|
+
export const invalidateObject = cjsProxyFacade.invalidateObject;
|
|
17
|
+
export const getGlobalOrig = cjsProxyFacade.getGlobalOrig;
|
|
18
|
+
|
|
19
|
+
import cjsOrigChangeTracking from "./origChangeTracking"
|
|
20
|
+
export const deleteProperty = cjsOrigChangeTracking.deleteProperty;
|
|
21
|
+
export const changeTrackedOrigObjects = cjsOrigChangeTracking.changeTrackedOrigObjects;
|
|
22
|
+
export const installChangeTracker = cjsOrigChangeTracking.installChangeTracker;
|
|
23
|
+
|
|
24
|
+
import cjsClassTrackers_Array from "./class-trackers/Array";
|
|
25
|
+
export const RecordedArrayValuesRead = cjsClassTrackers_Array.RecordedArrayValuesRead
|
|
26
|
+
|
|
27
|
+
import cjsClassTrackers_Map from "./class-trackers/Map";
|
|
28
|
+
export const RecordedMap_has = cjsClassTrackers_Map.RecordedMap_has;
|
|
29
|
+
export const RecordedMap_get = cjsClassTrackers_Map.RecordedMap_get;
|
|
30
|
+
export const RecordedMapEntriesRead = cjsClassTrackers_Map.RecordedMapEntriesRead;
|
|
31
|
+
export const RecordedMapKeysRead = cjsClassTrackers_Map.RecordedMapKeysRead;
|
|
32
|
+
export const RecordedMapValuesRead = cjsClassTrackers_Map.RecordedMapValuesRead;
|
|
33
|
+
|
|
34
|
+
import cjsClassTrackers_Set from "./class-trackers/Set";
|
|
35
|
+
export const RecordedSet_has = cjsClassTrackers_Set.RecordedSet_has;
|
|
36
|
+
export const RecordedSetValuesRead = cjsClassTrackers_Set.RecordedSetValuesRead;
|
|
37
|
+
|
|
38
|
+
import cjsWatchedProxyFacade from "./watchedProxyFacade"
|
|
39
|
+
export const RecordedValueRead = cjsWatchedProxyFacade.RecordedValueRead;
|
|
40
|
+
export const RecordedUnspecificRead = cjsWatchedProxyFacade.RecordedUnspecificRead;
|
|
41
|
+
export const RecordedPropertyRead = cjsWatchedProxyFacade.RecordedPropertyRead;
|
|
42
|
+
export const RecordedOwnKeysRead = cjsWatchedProxyFacade.RecordedOwnKeysRead;
|
|
43
|
+
export const WatchedProxyFacade = cjsWatchedProxyFacade.WatchedProxyFacade;
|
|
44
|
+
export const WatchedProxyHandler = cjsWatchedProxyFacade.WatchedProxyHandler;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ClassTrackingConfiguration, EventHook, ObjKey } from "./common";
|
|
2
|
+
/**
|
|
3
|
+
* Contains change listeners for one specific object.
|
|
4
|
+
* Note for specificity: There will be only one of the **change** events fired. The Recorded...Read.onChange handler will add the listeners to all possible candidates. It's this way around.
|
|
5
|
+
* Does not apply to setterInvoke.. These are fired in addition (not thought through for all situations)
|
|
6
|
+
*/
|
|
7
|
+
declare class ObjectChangeHooks {
|
|
8
|
+
/**
|
|
9
|
+
* For writes on **setters** (also if these are the same/unchanged values)
|
|
10
|
+
*/
|
|
11
|
+
setterInvoke: import("./Util").DefaultMap<ObjKey, EventHook>;
|
|
12
|
+
changeSpecificProperty: import("./Util").DefaultMap<ObjKey, EventHook>;
|
|
13
|
+
changeAnyProperty: EventHook;
|
|
14
|
+
/**
|
|
15
|
+
* Means, the result of Object.keys will be different after the change. All iterations over the object/arrays's keys or values are informed that there was a change. Individual {@link changeSpecificProperty} are not affected!
|
|
16
|
+
*/
|
|
17
|
+
changeOwnKeys: EventHook;
|
|
18
|
+
/**
|
|
19
|
+
* These will always be called, no matter how specific a change is
|
|
20
|
+
*/
|
|
21
|
+
anyChange: EventHook;
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
*/
|
|
25
|
+
unspecificChange: EventHook;
|
|
26
|
+
}
|
|
27
|
+
export declare const changeHooksForObject: WeakMap<object, ObjectChangeHooks>;
|
|
28
|
+
export declare function getChangeHooksForObject(obj: object): ObjectChangeHooks;
|
|
29
|
+
export declare class ObjectProxyHandler implements ProxyHandler<object> {
|
|
30
|
+
target: object;
|
|
31
|
+
origPrototype: object | null;
|
|
32
|
+
proxy: object;
|
|
33
|
+
trackingConfig?: ClassTrackingConfiguration;
|
|
34
|
+
constructor(target: object, trackingConfig: ClassTrackingConfiguration | undefined);
|
|
35
|
+
installSetterTrap(key: ObjKey): void;
|
|
36
|
+
protected withUnspecificChange<R>(changeFn: () => R): R;
|
|
37
|
+
get(fake_target: object, key: ObjKey, receiver: any): any;
|
|
38
|
+
set(fake_target: object, key: ObjKey, value: any, receiver: any): boolean;
|
|
39
|
+
getPrototypeOf(target: object): object | null;
|
|
40
|
+
defineProperty(target: object, property: string | symbol, attributes: PropertyDescriptor): boolean;
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=objectChangeTracking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objectChangeTracking.d.ts","sourceRoot":"","sources":["objectChangeTracking.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,0BAA0B,EAC1B,SAAS,EAGT,MAAM,EAIT,MAAM,UAAU,CAAC;AAElB;;;;GAIG;AACH,cAAM,iBAAiB;IACnB;;OAEG;IACH,YAAY,iDAA4D;IACxE,sBAAsB,iDAA4D;IAClF,iBAAiB,YAAmB;IAEpC;;OAEG;IACH,aAAa,YAAmB;IAChC;;OAEG;IACH,SAAS,YAAmB;IAE5B;;OAEG;IACH,gBAAgB,YAAmB;CACtC;AAID,eAAO,MAAM,oBAAoB,oCAA2C,CAAC;AAC7E,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,qBAMlD;AAED,qBAAa,kBAAmB,YAAW,YAAY,CAAC,MAAM,CAAC;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,0BAA0B,CAAA;gBAE/B,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,0BAA0B,GAAG,SAAS;IAmBlF,iBAAiB,CAAC,GAAG,EAAE,MAAM;IAkE7B,SAAS,CAAC,oBAAoB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC;IAIvD,GAAG,CAAC,WAAW,EAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAC,GAAG,GAAG,GAAG;IA2EvD,GAAG,CAAC,WAAW,EAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAC,GAAG,EAAE,QAAQ,EAAC,GAAG;IAkB5D,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI7C,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,kBAAkB,GAAG,OAAO;CAIrG"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ObjectProxyHandler = exports.changeHooksForObject = void 0;
|
|
4
|
+
exports.getChangeHooksForObject = getChangeHooksForObject;
|
|
5
|
+
/**
|
|
6
|
+
* Listeners for one object
|
|
7
|
+
*/
|
|
8
|
+
const Util_1 = require("./Util");
|
|
9
|
+
const common_1 = require("./common");
|
|
10
|
+
/**
|
|
11
|
+
* Contains change listeners for one specific object.
|
|
12
|
+
* Note for specificity: There will be only one of the **change** events fired. The Recorded...Read.onChange handler will add the listeners to all possible candidates. It's this way around.
|
|
13
|
+
* Does not apply to setterInvoke.. These are fired in addition (not thought through for all situations)
|
|
14
|
+
*/
|
|
15
|
+
class ObjectChangeHooks {
|
|
16
|
+
constructor() {
|
|
17
|
+
/**
|
|
18
|
+
* For writes on **setters** (also if these are the same/unchanged values)
|
|
19
|
+
*/
|
|
20
|
+
this.setterInvoke = (0, Util_1.newDefaultMap)(() => new common_1.EventHook());
|
|
21
|
+
this.changeSpecificProperty = (0, Util_1.newDefaultMap)(() => new common_1.EventHook());
|
|
22
|
+
this.changeAnyProperty = new common_1.EventHook();
|
|
23
|
+
/**
|
|
24
|
+
* Means, the result of Object.keys will be different after the change. All iterations over the object/arrays's keys or values are informed that there was a change. Individual {@link changeSpecificProperty} are not affected!
|
|
25
|
+
*/
|
|
26
|
+
this.changeOwnKeys = new common_1.EventHook();
|
|
27
|
+
/**
|
|
28
|
+
* These will always be called, no matter how specific a change is
|
|
29
|
+
*/
|
|
30
|
+
this.anyChange = new common_1.EventHook();
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
34
|
+
this.unspecificChange = new common_1.EventHook();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.changeHooksForObject = new WeakMap();
|
|
38
|
+
function getChangeHooksForObject(obj) {
|
|
39
|
+
let result = exports.changeHooksForObject.get(obj);
|
|
40
|
+
if (result === undefined) {
|
|
41
|
+
exports.changeHooksForObject.set(obj, result = new ObjectChangeHooks());
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
class ObjectProxyHandler {
|
|
46
|
+
constructor(target, trackingConfig) {
|
|
47
|
+
this.target = target;
|
|
48
|
+
this.trackingConfig = trackingConfig;
|
|
49
|
+
this.origPrototype = Object.getPrototypeOf(target);
|
|
50
|
+
Object.getOwnPropertyNames(target).forEach(key => {
|
|
51
|
+
if (key === "length" && Array.isArray(target)) {
|
|
52
|
+
return; // Leave the length property as is. It won't be set directly anyway
|
|
53
|
+
}
|
|
54
|
+
this.installSetterTrap(key);
|
|
55
|
+
});
|
|
56
|
+
// Create proxy:
|
|
57
|
+
//const targetForProxy = {}; // The virtual way
|
|
58
|
+
const targetForProxy = target; // Preserves Object.keys and instanceof behaviour :), iterators and other stuff. But the downside with this is, that it does not allow to proxy read only properties
|
|
59
|
+
this.proxy = new Proxy(targetForProxy, this);
|
|
60
|
+
}
|
|
61
|
+
installSetterTrap(key) {
|
|
62
|
+
let target = this.target;
|
|
63
|
+
let origDescriptor = (0, common_1.getPropertyDescriptor)(target, key);
|
|
64
|
+
//@ts-ignore
|
|
65
|
+
let currentValue = (origDescriptor === null || origDescriptor === void 0 ? void 0 : origDescriptor.value /* performance */) || target[key];
|
|
66
|
+
const origSetter = origDescriptor === null || origDescriptor === void 0 ? void 0 : origDescriptor.set;
|
|
67
|
+
const origGetter = origDescriptor === null || origDescriptor === void 0 ? void 0 : origDescriptor.get;
|
|
68
|
+
let origOwnDescriptor = Object.getOwnPropertyDescriptor(target, key);
|
|
69
|
+
if (origOwnDescriptor !== undefined) {
|
|
70
|
+
if (origOwnDescriptor.configurable !== true) {
|
|
71
|
+
throw new Error("Cannot delete non- 'configurable' property:" + String(key));
|
|
72
|
+
}
|
|
73
|
+
//@ts-ignore
|
|
74
|
+
delete target[key]; // delete the old, or the following Object.defineProperty will conflict
|
|
75
|
+
}
|
|
76
|
+
const newSetter = (newValue) => {
|
|
77
|
+
const changeHooksForTarget = getChangeHooksForObject(target);
|
|
78
|
+
if (origSetter !== undefined) {
|
|
79
|
+
(0, common_1.runChangeOperation)(target, new common_1.UnspecificObjectChange(target), [changeHooksForTarget.setterInvoke.get(key)], () => {
|
|
80
|
+
origSetter.apply(target, [newValue]); // call the setter
|
|
81
|
+
});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (origGetter !== undefined) {
|
|
85
|
+
currentValue = origGetter.apply(target); // call the getter. Is this a good idea to refresh the value here?
|
|
86
|
+
throw new TypeError("Target originally had a getter and no setter but the property is set.");
|
|
87
|
+
}
|
|
88
|
+
//@ts-ignore
|
|
89
|
+
if (newValue !== currentValue) { // modify ?
|
|
90
|
+
// run change operation and call listeners:
|
|
91
|
+
const hooksToServe = [];
|
|
92
|
+
if (Array.isArray(target)) {
|
|
93
|
+
hooksToServe.push(changeHooksForTarget.unspecificChange);
|
|
94
|
+
}
|
|
95
|
+
hooksToServe.push(changeHooksForTarget.changeSpecificProperty.get(key));
|
|
96
|
+
hooksToServe.push(changeHooksForTarget.changeAnyProperty);
|
|
97
|
+
(0, common_1.runChangeOperation)(target, new common_1.UnspecificObjectChange(target), hooksToServe, () => {
|
|
98
|
+
//@ts-ignore
|
|
99
|
+
currentValue = newValue;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
newSetter.origHadSetter = origSetter !== undefined;
|
|
104
|
+
const newGetter = () => {
|
|
105
|
+
if (origGetter !== undefined) {
|
|
106
|
+
currentValue = origGetter.apply(target); // call the getter
|
|
107
|
+
}
|
|
108
|
+
return currentValue;
|
|
109
|
+
};
|
|
110
|
+
newGetter.origHadGetter = origGetter !== undefined;
|
|
111
|
+
Object.defineProperty(target, key, {
|
|
112
|
+
set: newSetter,
|
|
113
|
+
get: newGetter,
|
|
114
|
+
enumerable: origOwnDescriptor !== undefined ? origOwnDescriptor === null || origOwnDescriptor === void 0 ? void 0 : origOwnDescriptor.enumerable : true,
|
|
115
|
+
configurable: true, // Allow to delete the property. Note that you should use the {@link deleteProperty} function
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
withUnspecificChange(changeFn) {
|
|
119
|
+
return (0, common_1.runChangeOperation)(this.target, new common_1.UnspecificObjectChange(this.target), [getChangeHooksForObject(this.target).unspecificChange], changeFn);
|
|
120
|
+
}
|
|
121
|
+
get(fake_target, key, receiver) {
|
|
122
|
+
var _a, _b, _c;
|
|
123
|
+
// Validity check
|
|
124
|
+
const target = this.target;
|
|
125
|
+
if (receiver !== target) {
|
|
126
|
+
throw new Error("Invalid state. Get was called on a different object than this write-tracker-proxy (which is set as the prototype) is for. Did you clone the object, resulting in shared prototypes?");
|
|
127
|
+
}
|
|
128
|
+
// Check for and use change tracker class:
|
|
129
|
+
const changeTrackerClass = (_a = this.trackingConfig) === null || _a === void 0 ? void 0 : _a.changeTracker;
|
|
130
|
+
if (changeTrackerClass !== undefined) {
|
|
131
|
+
let propOnSupervisor = Object.getOwnPropertyDescriptor(changeTrackerClass.prototype, key);
|
|
132
|
+
if (propOnSupervisor !== undefined) { // Supervisor class is responsible for the property (or method) ?
|
|
133
|
+
//@ts-ignore
|
|
134
|
+
if (propOnSupervisor.get) { // Prop is a getter?
|
|
135
|
+
return propOnSupervisor.get.apply(target);
|
|
136
|
+
}
|
|
137
|
+
else if (propOnSupervisor.value) { // Prop is a value, meaning a function. (Supervisors don't have fields)
|
|
138
|
+
return changeTrackerClass.prototype[key];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
const origValue = changeTrackerClass.prototype[key];
|
|
143
|
+
if (typeof origValue === "function") {
|
|
144
|
+
origMethod = origValue;
|
|
145
|
+
if ((_b = this.trackingConfig) === null || _b === void 0 ? void 0 : _b.knownHighLevelMethods.has(key)) {
|
|
146
|
+
return trapForHighLevelWriterMethod;
|
|
147
|
+
}
|
|
148
|
+
else if (!((_c = this.trackingConfig) === null || _c === void 0 ? void 0 : _c.readOnlyMethods.has(key)) && !(key in Object.prototype)) { // Read-write method that was not handled directly by change tracker class?
|
|
149
|
+
return trapForGenericWriterMethod; // Assume the worst, that it is a writer method
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// return this.target[key]; // This line does not work because it does not consult ObjectProxyHandler#getPrototypeOf and therefore uses the actual tinkered prototype chain which has this proxy in there and calls get (endless recursion)
|
|
155
|
+
const propDesc = (0, common_1.getPropertyDescriptor)(target, key);
|
|
156
|
+
if (propDesc !== undefined) {
|
|
157
|
+
let result;
|
|
158
|
+
let getter = propDesc.get;
|
|
159
|
+
if (getter !== undefined) {
|
|
160
|
+
result = getter.apply(target);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
result = propDesc.value;
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
var origMethod = undefined;
|
|
168
|
+
/**
|
|
169
|
+
* Calls the unspecificChange listeners
|
|
170
|
+
* @param args
|
|
171
|
+
*/
|
|
172
|
+
function trapForGenericWriterMethod(...args) {
|
|
173
|
+
if (this !== receiver) {
|
|
174
|
+
//throw new Error("Invalid state. Method was called on invalid target")
|
|
175
|
+
}
|
|
176
|
+
return (0, common_1.runChangeOperation)(target, new common_1.UnspecificObjectChange(target), [getChangeHooksForObject(target).unspecificChange], () => {
|
|
177
|
+
return origMethod.apply(this, args); // call original method
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Wraps it in runAndCallListenersOnce_after
|
|
182
|
+
* @param args
|
|
183
|
+
*/
|
|
184
|
+
function trapForHighLevelWriterMethod(...args) {
|
|
185
|
+
if (this !== receiver) {
|
|
186
|
+
//throw new Error("Invalid state. Method was called on invalid target")
|
|
187
|
+
}
|
|
188
|
+
return (0, common_1.runChangeOperation)(target, undefined, [], () => {
|
|
189
|
+
return origMethod.apply(this, args); // call original method
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
set(fake_target, key, value, receiver) {
|
|
194
|
+
// Validity check
|
|
195
|
+
if (receiver !== this.target) {
|
|
196
|
+
throw new Error("Invalid state. Set was called on a different object than this write-tracker-proxy (which is set as the prototype) is for. Did you clone the object, resulting in shared prototypes?");
|
|
197
|
+
}
|
|
198
|
+
(0, common_1.runChangeOperation)(this.target, new common_1.UnspecificObjectChange(this.target), [getChangeHooksForObject(this.target).changeOwnKeys], () => {
|
|
199
|
+
// if this "set" method got called, there is no setter trap installed yet
|
|
200
|
+
this.installSetterTrap(key);
|
|
201
|
+
//@ts-ignore
|
|
202
|
+
this.target[key] = value; // Set value again. this should call the setter trap
|
|
203
|
+
});
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
getPrototypeOf(target) {
|
|
207
|
+
return this.origPrototype;
|
|
208
|
+
}
|
|
209
|
+
defineProperty(target, property, attributes) {
|
|
210
|
+
throw new Error("Defineproperty not yet supported");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
exports.ObjectProxyHandler = ObjectProxyHandler;
|
|
214
|
+
//# sourceMappingURL=objectChangeTracking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objectChangeTracking.js","sourceRoot":"","sources":["objectChangeTracking.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Listeners for one object
|
|
3
|
+
*/
|
|
4
|
+
import {newDefaultMap} from "./Util";
|
|
5
|
+
import {
|
|
6
|
+
ClassTrackingConfiguration,
|
|
7
|
+
EventHook,
|
|
8
|
+
getPropertyDescriptor,
|
|
9
|
+
GetterFlags,
|
|
10
|
+
ObjKey,
|
|
11
|
+
runChangeOperation,
|
|
12
|
+
SetterFlags,
|
|
13
|
+
UnspecificObjectChange,
|
|
14
|
+
} from "./common";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Contains change listeners for one specific object.
|
|
18
|
+
* Note for specificity: There will be only one of the **change** events fired. The Recorded...Read.onChange handler will add the listeners to all possible candidates. It's this way around.
|
|
19
|
+
* Does not apply to setterInvoke.. These are fired in addition (not thought through for all situations)
|
|
20
|
+
*/
|
|
21
|
+
class ObjectChangeHooks {
|
|
22
|
+
/**
|
|
23
|
+
* For writes on **setters** (also if these are the same/unchanged values)
|
|
24
|
+
*/
|
|
25
|
+
setterInvoke = newDefaultMap<ObjKey, EventHook>( () => new EventHook());
|
|
26
|
+
changeSpecificProperty = newDefaultMap<ObjKey, EventHook>( () => new EventHook());
|
|
27
|
+
changeAnyProperty = new EventHook();
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Means, the result of Object.keys will be different after the change. All iterations over the object/arrays's keys or values are informed that there was a change. Individual {@link changeSpecificProperty} are not affected!
|
|
31
|
+
*/
|
|
32
|
+
changeOwnKeys = new EventHook();
|
|
33
|
+
/**
|
|
34
|
+
* These will always be called, no matter how specific a change is
|
|
35
|
+
*/
|
|
36
|
+
anyChange = new EventHook();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
*/
|
|
41
|
+
unspecificChange = new EventHook();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
export const changeHooksForObject = new WeakMap<object, ObjectChangeHooks>();
|
|
47
|
+
export function getChangeHooksForObject(obj: object) {
|
|
48
|
+
let result = changeHooksForObject.get(obj);
|
|
49
|
+
if(result === undefined) {
|
|
50
|
+
changeHooksForObject.set(obj, result = new ObjectChangeHooks());
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class ObjectProxyHandler implements ProxyHandler<object> {
|
|
56
|
+
target: object;
|
|
57
|
+
origPrototype: object | null;
|
|
58
|
+
proxy: object;
|
|
59
|
+
trackingConfig?: ClassTrackingConfiguration
|
|
60
|
+
|
|
61
|
+
constructor(target: object, trackingConfig: ClassTrackingConfiguration | undefined) {
|
|
62
|
+
this.target = target;
|
|
63
|
+
this.trackingConfig = trackingConfig;
|
|
64
|
+
this.origPrototype = Object.getPrototypeOf(target);
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
Object.getOwnPropertyNames(target).forEach(key => {
|
|
68
|
+
if(key === "length" && Array.isArray(target)) {
|
|
69
|
+
return; // Leave the length property as is. It won't be set directly anyway
|
|
70
|
+
}
|
|
71
|
+
this.installSetterTrap(key)
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Create proxy:
|
|
75
|
+
//const targetForProxy = {}; // The virtual way
|
|
76
|
+
const targetForProxy=target // Preserves Object.keys and instanceof behaviour :), iterators and other stuff. But the downside with this is, that it does not allow to proxy read only properties
|
|
77
|
+
this.proxy = new Proxy(targetForProxy, this);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
installSetterTrap(key: ObjKey) {
|
|
81
|
+
let target = this.target;
|
|
82
|
+
let origDescriptor = getPropertyDescriptor(target, key);
|
|
83
|
+
//@ts-ignore
|
|
84
|
+
let currentValue = origDescriptor?.value /* performance */ || target[key];
|
|
85
|
+
const origSetter = origDescriptor?.set;
|
|
86
|
+
const origGetter = origDescriptor?.get;
|
|
87
|
+
|
|
88
|
+
let origOwnDescriptor = Object.getOwnPropertyDescriptor(target, key);
|
|
89
|
+
if(origOwnDescriptor !== undefined) {
|
|
90
|
+
if(origOwnDescriptor.configurable !== true) {
|
|
91
|
+
throw new Error("Cannot delete non- 'configurable' property:" + String(key));
|
|
92
|
+
}
|
|
93
|
+
//@ts-ignore
|
|
94
|
+
delete target[key]; // delete the old, or the following Object.defineProperty will conflict
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const newSetter= (newValue: any) => {
|
|
98
|
+
const changeHooksForTarget = getChangeHooksForObject(target);
|
|
99
|
+
|
|
100
|
+
if(origSetter !== undefined) {
|
|
101
|
+
runChangeOperation(target, new UnspecificObjectChange(target),[changeHooksForTarget.setterInvoke.get(key)],() => {
|
|
102
|
+
origSetter.apply(target, [newValue]); // call the setter
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if(origGetter !== undefined) {
|
|
108
|
+
currentValue = origGetter.apply(target); // call the getter. Is this a good idea to refresh the value here?
|
|
109
|
+
throw new TypeError("Target originally had a getter and no setter but the property is set.");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//@ts-ignore
|
|
113
|
+
if (newValue !== currentValue) { // modify ?
|
|
114
|
+
// run change operation and call listeners:
|
|
115
|
+
const hooksToServe: EventHook[] = [];
|
|
116
|
+
if(Array.isArray(target)) {
|
|
117
|
+
hooksToServe.push(changeHooksForTarget.unspecificChange);
|
|
118
|
+
}
|
|
119
|
+
hooksToServe.push(changeHooksForTarget.changeSpecificProperty.get(key))
|
|
120
|
+
hooksToServe.push(changeHooksForTarget.changeAnyProperty)
|
|
121
|
+
runChangeOperation(target, new UnspecificObjectChange(target),hooksToServe,() => {
|
|
122
|
+
//@ts-ignore
|
|
123
|
+
currentValue = newValue;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}
|
|
128
|
+
(newSetter as SetterFlags).origHadSetter = origSetter !== undefined;
|
|
129
|
+
|
|
130
|
+
const newGetter = () => {
|
|
131
|
+
if(origGetter !== undefined) {
|
|
132
|
+
currentValue = origGetter.apply(target); // call the getter
|
|
133
|
+
}
|
|
134
|
+
return currentValue;
|
|
135
|
+
}
|
|
136
|
+
(newGetter as GetterFlags).origHadGetter = origGetter !== undefined;
|
|
137
|
+
|
|
138
|
+
Object.defineProperty( target, key, { // TODO: [Performance optimization tipps, see js example](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#description)
|
|
139
|
+
set: newSetter,
|
|
140
|
+
get: newGetter,
|
|
141
|
+
enumerable: origOwnDescriptor !== undefined?origOwnDescriptor?.enumerable:true,
|
|
142
|
+
configurable: true, // Allow to delete the property. Note that you should use the {@link deleteProperty} function
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
protected withUnspecificChange<R>(changeFn: () => R): R {
|
|
147
|
+
return runChangeOperation(this.target, new UnspecificObjectChange(this.target), [getChangeHooksForObject(this.target).unspecificChange], changeFn)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
get(fake_target:object, key: ObjKey, receiver:any): any {
|
|
151
|
+
// Validity check
|
|
152
|
+
const target = this.target;
|
|
153
|
+
|
|
154
|
+
if(receiver !== target) {
|
|
155
|
+
throw new Error("Invalid state. Get was called on a different object than this write-tracker-proxy (which is set as the prototype) is for. Did you clone the object, resulting in shared prototypes?")
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check for and use change tracker class:
|
|
159
|
+
const changeTrackerClass = this.trackingConfig?.changeTracker
|
|
160
|
+
if (changeTrackerClass !== undefined) {
|
|
161
|
+
let propOnSupervisor = Object.getOwnPropertyDescriptor(changeTrackerClass.prototype, key);
|
|
162
|
+
if (propOnSupervisor !== undefined) { // Supervisor class is responsible for the property (or method) ?
|
|
163
|
+
//@ts-ignore
|
|
164
|
+
if (propOnSupervisor.get) { // Prop is a getter?
|
|
165
|
+
return propOnSupervisor.get.apply(target)
|
|
166
|
+
} else if (propOnSupervisor.value) { // Prop is a value, meaning a function. (Supervisors don't have fields)
|
|
167
|
+
return changeTrackerClass.prototype[key];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
const origValue = changeTrackerClass.prototype[key]
|
|
172
|
+
if(typeof origValue === "function") {
|
|
173
|
+
origMethod = origValue;
|
|
174
|
+
if (this.trackingConfig?.knownHighLevelMethods.has(key)) {
|
|
175
|
+
return trapForHighLevelWriterMethod
|
|
176
|
+
} else if (!this.trackingConfig?.readOnlyMethods.has(key) && !(key as any in Object.prototype)) { // Read-write method that was not handled directly by change tracker class?
|
|
177
|
+
return trapForGenericWriterMethod // Assume the worst, that it is a writer method
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// return this.target[key]; // This line does not work because it does not consult ObjectProxyHandler#getPrototypeOf and therefore uses the actual tinkered prototype chain which has this proxy in there and calls get (endless recursion)
|
|
184
|
+
const propDesc = getPropertyDescriptor(target, key)
|
|
185
|
+
if (propDesc !== undefined) {
|
|
186
|
+
let result: unknown;
|
|
187
|
+
let getter = propDesc.get;
|
|
188
|
+
if (getter !== undefined) {
|
|
189
|
+
result = getter.apply(target);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
result = propDesc.value;
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
var origMethod: ((this:unknown, ...args:unknown[]) => unknown) | undefined = undefined;
|
|
198
|
+
/**
|
|
199
|
+
* Calls the unspecificChange listeners
|
|
200
|
+
* @param args
|
|
201
|
+
*/
|
|
202
|
+
function trapForGenericWriterMethod(this:object, ...args: unknown[]) {
|
|
203
|
+
if(this !== receiver) {
|
|
204
|
+
//throw new Error("Invalid state. Method was called on invalid target")
|
|
205
|
+
}
|
|
206
|
+
return runChangeOperation(target, new UnspecificObjectChange(target),[getChangeHooksForObject(target as Array<unknown>).unspecificChange],() => {
|
|
207
|
+
return origMethod!.apply(this, args); // call original method
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Wraps it in runAndCallListenersOnce_after
|
|
213
|
+
* @param args
|
|
214
|
+
*/
|
|
215
|
+
function trapForHighLevelWriterMethod(this:object, ...args: unknown[]) {
|
|
216
|
+
if(this !== receiver) {
|
|
217
|
+
//throw new Error("Invalid state. Method was called on invalid target")
|
|
218
|
+
}
|
|
219
|
+
return runChangeOperation(target, undefined,[],() => {
|
|
220
|
+
return origMethod!.apply(this, args); // call original method
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
set(fake_target:object, key: ObjKey, value:any, receiver:any) {
|
|
226
|
+
// Validity check
|
|
227
|
+
if(receiver !== this.target) {
|
|
228
|
+
throw new Error("Invalid state. Set was called on a different object than this write-tracker-proxy (which is set as the prototype) is for. Did you clone the object, resulting in shared prototypes?")
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
runChangeOperation(this.target, new UnspecificObjectChange(this.target),[getChangeHooksForObject(this.target).changeOwnKeys],() => { // There was no setter trap yet. This means that the key is new. Inform those listeners:
|
|
232
|
+
|
|
233
|
+
// if this "set" method got called, there is no setter trap installed yet
|
|
234
|
+
this.installSetterTrap(key);
|
|
235
|
+
|
|
236
|
+
//@ts-ignore
|
|
237
|
+
this.target[key] = value; // Set value again. this should call the setter trap
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
getPrototypeOf(target: object): object | null {
|
|
244
|
+
return this.origPrototype;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
defineProperty(target: object, property: string | symbol, attributes: PropertyDescriptor): boolean {
|
|
248
|
+
throw new Error("Defineproperty not yet supported");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PartialGraph } from "./common";
|
|
2
|
+
export declare const changeTrackedOrigObjects: PartialGraph;
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param obj
|
|
6
|
+
*/
|
|
7
|
+
export declare function installChangeTracker(obj: object): void;
|
|
8
|
+
/**
|
|
9
|
+
* Use this to delete properties on objects that have a write tracker installed. Otherwise they are not deletable and the write tracker cannot track the object's keys modification and inform listeners
|
|
10
|
+
* @param obj
|
|
11
|
+
* @param key
|
|
12
|
+
*/
|
|
13
|
+
export declare function deleteProperty<O extends object>(obj: O, key: keyof O): boolean;
|
|
14
|
+
//# sourceMappingURL=origChangeTracking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origChangeTracking.d.ts","sourceRoot":"","sources":["origChangeTracking.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,YAAY,EAA6C,MAAM,UAAU,CAAC;AAUlF,eAAO,MAAM,wBAAwB,cAAqB,CAAC;AAI3D;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,QA2B/C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,WAepE"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// This file has functions / classes that allow to watch writes to the original unproxied objects (or arrays/sets/maps)
|
|
3
|
+
// unproxied=not part of a proxy facade. Technically this can install Proxys as the prototype, to catch writes.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.changeTrackedOrigObjects = void 0;
|
|
6
|
+
exports.installChangeTracker = installChangeTracker;
|
|
7
|
+
exports.deleteProperty = deleteProperty;
|
|
8
|
+
const common_1 = require("./common");
|
|
9
|
+
const objectChangeTracking_1 = require("./objectChangeTracking");
|
|
10
|
+
const index_1 = require("./class-trackers/index");
|
|
11
|
+
const proxyFacade_1 = require("./proxyFacade");
|
|
12
|
+
const Util_1 = require("./Util");
|
|
13
|
+
exports.changeTrackedOrigObjects = new common_1.PartialGraph();
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param obj
|
|
17
|
+
*/
|
|
18
|
+
function installChangeTracker(obj) {
|
|
19
|
+
!(0, proxyFacade_1.isProxyForAFacade)(obj) || (0, Util_1.throwError)("Cannot install change tracker on a proxy. The proxy should already support change tracking.");
|
|
20
|
+
if (exports.changeTrackedOrigObjects.hasObj(obj)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
function inner() {
|
|
24
|
+
const trackingConfig = (0, index_1.getTrackingConfigFor)(obj);
|
|
25
|
+
if (trackingConfig) {
|
|
26
|
+
if (trackingConfig.trackSettingObjectProperties) {
|
|
27
|
+
// Achieve this with the ObjectProxyhandler. It will consider getTrackingConfigFor(obj).changeTracker itsself:
|
|
28
|
+
const proxy = new objectChangeTracking_1.ObjectProxyHandler(obj, trackingConfig).proxy;
|
|
29
|
+
Object.setPrototypeOf(obj, proxy);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (trackingConfig.changeTracker !== undefined) {
|
|
33
|
+
Object.setPrototypeOf(obj, trackingConfig.changeTracker.prototype);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else { // Non-special object ?
|
|
37
|
+
const proxy = new objectChangeTracking_1.ObjectProxyHandler(obj, trackingConfig).proxy;
|
|
38
|
+
Object.setPrototypeOf(obj, proxy);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
inner();
|
|
42
|
+
exports.changeTrackedOrigObjects._register(obj);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Use this to delete properties on objects that have a write tracker installed. Otherwise they are not deletable and the write tracker cannot track the object's keys modification and inform listeners
|
|
46
|
+
* @param obj
|
|
47
|
+
* @param key
|
|
48
|
+
*/
|
|
49
|
+
function deleteProperty(obj, key) {
|
|
50
|
+
if (!exports.changeTrackedOrigObjects.hasObj(obj)) {
|
|
51
|
+
return delete obj[key];
|
|
52
|
+
}
|
|
53
|
+
const doesExist = Object.getOwnPropertyDescriptor(obj, key) !== undefined;
|
|
54
|
+
if (!doesExist) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return (0, common_1.runChangeOperation)(obj, new common_1.UnspecificObjectChange(obj), [(0, objectChangeTracking_1.getChangeHooksForObject)(obj).changeOwnKeys], () => {
|
|
58
|
+
//@ts-ignore
|
|
59
|
+
obj[key] = undefined; // Set to undefined first, so property change listeners will get informed
|
|
60
|
+
return delete obj[key];
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=origChangeTracking.js.map
|