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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origChangeTracking.js","sourceRoot":"","sources":["origChangeTracking.ts"],"names":[],"mappings":";AAAA,uHAAuH;AACvH,+GAA+G;;;AAqB/G,oDA2BC;AAOD,wCAeC;AAnED,qCAAkF;AAClF,iEAIgC;AAChC,kDAA4D;AAC5D,+CAAgD;AAChD,iCAAkC;AAErB,QAAA,wBAAwB,GAAG,IAAI,qBAAY,EAAE,CAAC;AAI3D;;;GAGG;AACH,SAAgB,oBAAoB,CAAC,GAAW;IAC5C,CAAC,IAAA,+BAAiB,EAAC,GAAG,CAAC,IAAI,IAAA,iBAAU,EAAC,6FAA6F,CAAC,CAAC;IACrI,IAAG,gCAAwB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,OAAO;IACX,CAAC;IAED,SAAS,KAAK;QACV,MAAM,cAAc,GAAG,IAAA,4BAAoB,EAAC,GAAG,CAAC,CAAC;QACjD,IAAI,cAAc,EAAE,CAAC;YACjB,IAAI,cAAc,CAAC,4BAA4B,EAAE,CAAC;gBAC9C,8GAA8G;gBAC9G,MAAM,KAAK,GAAG,IAAI,yCAAkB,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,KAAK,CAAC;gBAChE,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBAClC,OAAO;YACX,CAAC;YAED,IAAI,cAAc,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;gBAC7C,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;YACvE,CAAC;QACL,CAAC;aAAM,CAAC,CAAC,uBAAuB;YAC5B,MAAM,KAAK,GAAG,IAAI,yCAAkB,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,KAAK,CAAC;YAChE,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;IACL,CAAC;IACD,KAAK,EAAE,CAAC;IAER,gCAAwB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,SAAgB,cAAc,CAAmB,GAAM,EAAE,GAAY;IACjE,IAAG,CAAC,gCAAwB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,OAAO,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,SAAS,CAAC;IAC1E,IAAI,CAAC,SAAS,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,OAAO,IAAA,2BAAkB,EAAC,GAAG,EAAE,IAAI,+BAAsB,CAAC,GAAG,CAAC,EAAE,CAAC,IAAA,8CAAuB,EAAC,GAAG,CAAC,CAAC,aAAa,CAAC,EAAE,GAAG,EAAE;QAC/G,YAAY;QACZ,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,yEAAyE;QAC/F,OAAO,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// This file has functions / classes that allow to watch writes to the original unproxied objects (or arrays/sets/maps)
|
|
2
|
+
// unproxied=not part of a proxy facade. Technically this can install Proxys as the prototype, to catch writes.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import {PartialGraph, runChangeOperation, UnspecificObjectChange} from "./common";
|
|
6
|
+
import {
|
|
7
|
+
ObjectProxyHandler,
|
|
8
|
+
changeHooksForObject,
|
|
9
|
+
getChangeHooksForObject
|
|
10
|
+
} from "./objectChangeTracking";
|
|
11
|
+
import {getTrackingConfigFor} from "./class-trackers/index";
|
|
12
|
+
import {isProxyForAFacade} from "./proxyFacade";
|
|
13
|
+
import {throwError} from "./Util";
|
|
14
|
+
|
|
15
|
+
export const changeTrackedOrigObjects = new PartialGraph();
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @param obj
|
|
22
|
+
*/
|
|
23
|
+
export function installChangeTracker(obj: object) {
|
|
24
|
+
!isProxyForAFacade(obj) || throwError("Cannot install change tracker on a proxy. The proxy should already support change tracking.");
|
|
25
|
+
if(changeTrackedOrigObjects.hasObj(obj)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function inner() {
|
|
30
|
+
const trackingConfig = getTrackingConfigFor(obj);
|
|
31
|
+
if (trackingConfig) {
|
|
32
|
+
if (trackingConfig.trackSettingObjectProperties) {
|
|
33
|
+
// Achieve this with the ObjectProxyhandler. It will consider getTrackingConfigFor(obj).changeTracker itsself:
|
|
34
|
+
const proxy = new ObjectProxyHandler(obj, trackingConfig).proxy;
|
|
35
|
+
Object.setPrototypeOf(obj, proxy);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (trackingConfig.changeTracker !== undefined) {
|
|
40
|
+
Object.setPrototypeOf(obj, trackingConfig.changeTracker.prototype);
|
|
41
|
+
}
|
|
42
|
+
} else { // Non-special object ?
|
|
43
|
+
const proxy = new ObjectProxyHandler(obj, trackingConfig).proxy;
|
|
44
|
+
Object.setPrototypeOf(obj, proxy);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
inner();
|
|
48
|
+
|
|
49
|
+
changeTrackedOrigObjects._register(obj);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 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
|
|
54
|
+
* @param obj
|
|
55
|
+
* @param key
|
|
56
|
+
*/
|
|
57
|
+
export function deleteProperty<O extends object>(obj: O, key: keyof O) {
|
|
58
|
+
if(!changeTrackedOrigObjects.hasObj(obj)) {
|
|
59
|
+
return delete obj[key];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const doesExist = Object.getOwnPropertyDescriptor(obj, key) !== undefined;
|
|
63
|
+
if (!doesExist) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return runChangeOperation(obj, new UnspecificObjectChange(obj), [getChangeHooksForObject(obj).changeOwnKeys], () => {
|
|
68
|
+
//@ts-ignore
|
|
69
|
+
obj[key] = undefined; // Set to undefined first, so property change listeners will get informed
|
|
70
|
+
return delete obj[key];
|
|
71
|
+
});
|
|
72
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "proxy-facades",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"proxy",
|
|
7
|
+
"tracker",
|
|
8
|
+
"read-tracking",
|
|
9
|
+
"write-tracking"
|
|
10
|
+
],
|
|
11
|
+
"author": "Boris Gingold <bogeee@bogitech.de>",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/bogeeee/proxy-facades.git"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"main": "index.js",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": "./index_esm.mjs",
|
|
21
|
+
"require": "./index.js"
|
|
22
|
+
},
|
|
23
|
+
"./develop": {
|
|
24
|
+
"browser": "./index.ts"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"dev:playground.ts": "tsx devPlayground.ts",
|
|
29
|
+
"dev:generateEsRuntimeBehaviourCheckerCode": "tsx dev_generateEsRuntimeBehaviourCheckerCode.ts",
|
|
30
|
+
"clean": "tsc --build --clean && rimraf dist",
|
|
31
|
+
"build": "tsc --build --force && tsc -module ES6 --outDir dist/mjs",
|
|
32
|
+
"test": "npm run clean && vitest --clearScreen --hideSkippedTests --poolOptions.threads.singleThread",
|
|
33
|
+
"test_specific": "npm run clean && vitest --clearScreen --hideSkippedTests --poolOptions.threads.singleThread --testNamePattern \"add.*should behave normally. With writes through installed write tracker\"",
|
|
34
|
+
"prepublish": "npm run clean && npm run build",
|
|
35
|
+
"publish:npmlogin": "npm login",
|
|
36
|
+
"publish:publish": "npm run prepublish && npm publish"
|
|
37
|
+
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"underscore": "^1.13.3",
|
|
41
|
+
"@types/underscore": "^1.11.4",
|
|
42
|
+
"@types/node": "^20.12.7"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"tsx": "^4.7.0",
|
|
46
|
+
"@vitest/ui": "^1.5.1",
|
|
47
|
+
"rimraf": "=5.0.5",
|
|
48
|
+
"ncp": "=2.0.0",
|
|
49
|
+
"typescript": "^5.4.5",
|
|
50
|
+
"vitest": "^1.5.0"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/proxyFacade.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
*/
|
|
4
|
+
import { ObjKey, PartialGraph } from "./common";
|
|
5
|
+
export declare abstract class ProxyFacade<HANDLER extends FacadeProxyHandler<any>> extends PartialGraph {
|
|
6
|
+
/**
|
|
7
|
+
* Treats them like functions, meaning, they get a proxied 'this'. WatchProxies will see the access to the real properties
|
|
8
|
+
*/
|
|
9
|
+
propertyAccessorsAsWhiteBox: boolean;
|
|
10
|
+
protected objectsToProxyHandlers: WeakMap<object, HANDLER>;
|
|
11
|
+
protected abstract crateHandler(target: object, facade: any): HANDLER;
|
|
12
|
+
getProxyFor<O>(value: O): O;
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param value
|
|
16
|
+
* @return the original non-proxied value
|
|
17
|
+
*/
|
|
18
|
+
getUnproxiedValue<O>(value: O): O;
|
|
19
|
+
getHandlerFor(obj: object): HANDLER;
|
|
20
|
+
}
|
|
21
|
+
export declare abstract class FacadeProxyHandler<FACADE extends ProxyFacade<any>> implements ProxyHandler<object> {
|
|
22
|
+
target: object;
|
|
23
|
+
proxy: object;
|
|
24
|
+
facade: FACADE;
|
|
25
|
+
constructor(target: object, facade: FACADE);
|
|
26
|
+
deleteProperty(target: object, key: string | symbol): boolean;
|
|
27
|
+
defineProperty(target: object, property: string | symbol, attributes: PropertyDescriptor): boolean;
|
|
28
|
+
get(fake_target: object, p: string | symbol, receiver: any): any;
|
|
29
|
+
protected rawRead(key: ObjKey): unknown;
|
|
30
|
+
set(fake_target: object, p: string | symbol, value: any, receiver: any): boolean;
|
|
31
|
+
protected rawChange(p: string | symbol, newUnproxiedValue: any): void;
|
|
32
|
+
}
|
|
33
|
+
export declare function isProxyForAFacade(obj: object): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Makes the obj throw an error when trying to access it
|
|
36
|
+
* @param obj
|
|
37
|
+
* @param message
|
|
38
|
+
* @param cause
|
|
39
|
+
*/
|
|
40
|
+
export declare function invalidateObject(obj: object, message: string, cause?: Error): void;
|
|
41
|
+
/**
|
|
42
|
+
* @returns the real real origial object from the real world
|
|
43
|
+
*/
|
|
44
|
+
export declare function getGlobalOrig<T extends object>(obj: T): T;
|
|
45
|
+
//# sourceMappingURL=proxyFacade.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxyFacade.d.ts","sourceRoot":"","sources":["proxyFacade.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAIH,MAAM,EACN,YAAY,EAEf,MAAM,UAAU,CAAC;AAIlB,8BAAsB,WAAW,CAAC,OAAO,SAAS,kBAAkB,CAAC,GAAG,CAAC,CAAE,SAAQ,YAAY;IAE3F;;OAEG;IACI,2BAA2B,UAAQ;IAG1C,SAAS,CAAC,sBAAsB,2BAAkC;IAElE,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO;IAErE,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC;IAwB3B;;;;OAIG;IACH,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC;IAQjC,aAAa,CAAC,GAAG,EAAE,MAAM,GAC4B,OAAO;CAG/D;AAED,8BAAsB,kBAAkB,CAAC,MAAM,SAAS,WAAW,CAAC,GAAG,CAAC,CAAE,YAAW,YAAY,CAAC,MAAM,CAAC;IACrG,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;gBAEH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAU1C,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO;IAK7D,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,kBAAkB,GAAG,OAAO;IAIlG,GAAG,CAAE,WAAW,EAAC,MAAM,EAAE,CAAC,EAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAC,GAAG;IA+BxD,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAKvC,GAAG,CAAC,WAAW,EAAC,MAAM,EAAE,CAAC,EAAC,MAAM,GAAG,MAAM,EAAE,KAAK,EAAC,GAAG,EAAE,QAAQ,EAAC,GAAG;IAoBlE,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,iBAAiB,EAAE,GAAG;CAOjE;AAQD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,WAE5C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,QAoC3E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAMzD"}
|
package/proxyFacade.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FacadeProxyHandler = exports.ProxyFacade = void 0;
|
|
4
|
+
exports.isProxyForAFacade = isProxyForAFacade;
|
|
5
|
+
exports.invalidateObject = invalidateObject;
|
|
6
|
+
exports.getGlobalOrig = getGlobalOrig;
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
const common_1 = require("./common");
|
|
11
|
+
const origChangeTracking_1 = require("./origChangeTracking");
|
|
12
|
+
class ProxyFacade extends common_1.PartialGraph {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
// *** Configuration: ***
|
|
16
|
+
/**
|
|
17
|
+
* Treats them like functions, meaning, they get a proxied 'this'. WatchProxies will see the access to the real properties
|
|
18
|
+
*/
|
|
19
|
+
this.propertyAccessorsAsWhiteBox = true;
|
|
20
|
+
// *** State: ***
|
|
21
|
+
this.objectsToProxyHandlers = new WeakMap();
|
|
22
|
+
}
|
|
23
|
+
getProxyFor(value) {
|
|
24
|
+
if (value === null || typeof value !== "object") { // not an object?
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
if (this.hasObj(value)) { // Already our proxied object ?
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
let handlerForObj = this.objectsToProxyHandlers.get(value);
|
|
31
|
+
if (handlerForObj !== undefined) { // value was an unproxied object and we have the proxy for it ?
|
|
32
|
+
return handlerForObj.proxy;
|
|
33
|
+
}
|
|
34
|
+
handlerForObj = this.crateHandler(value, this);
|
|
35
|
+
// register:
|
|
36
|
+
proxyToProxyHandler.set(handlerForObj.proxy, handlerForObj);
|
|
37
|
+
this.objectsToProxyHandlers.set(value, handlerForObj);
|
|
38
|
+
this._register(handlerForObj.proxy);
|
|
39
|
+
return handlerForObj.proxy;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
* @param value
|
|
44
|
+
* @return the original non-proxied value
|
|
45
|
+
*/
|
|
46
|
+
getUnproxiedValue(value) {
|
|
47
|
+
var _a;
|
|
48
|
+
if (value === null || typeof value !== "object") { // not an object?
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
return ((_a = proxyToProxyHandler.get(value)) === null || _a === void 0 ? void 0 : _a.target) || value;
|
|
52
|
+
}
|
|
53
|
+
getHandlerFor(obj) {
|
|
54
|
+
return getProxyHandler(this.getProxyFor(obj));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.ProxyFacade = ProxyFacade;
|
|
58
|
+
class FacadeProxyHandler {
|
|
59
|
+
constructor(target, facade) {
|
|
60
|
+
this.target = target;
|
|
61
|
+
this.facade = facade;
|
|
62
|
+
// Create proxy:
|
|
63
|
+
//const targetForProxy = {}; // The virtual way
|
|
64
|
+
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
|
|
65
|
+
this.proxy = new Proxy(targetForProxy, this);
|
|
66
|
+
}
|
|
67
|
+
deleteProperty(target, key) {
|
|
68
|
+
//@ts-ignore
|
|
69
|
+
return (0, origChangeTracking_1.deleteProperty)(this.target, key);
|
|
70
|
+
}
|
|
71
|
+
defineProperty(target, property, attributes) {
|
|
72
|
+
throw new Error("Must not use defineProperty on a proxied object. Handling of change tracking etc. for this may not be implemented");
|
|
73
|
+
}
|
|
74
|
+
get(fake_target, p, receiver) {
|
|
75
|
+
var _a;
|
|
76
|
+
// Validity check
|
|
77
|
+
if (receiver !== this.proxy) {
|
|
78
|
+
throw new Error("Invalid state. Get was called on a different object than this proxy is for."); // Cannot imagine a legal case
|
|
79
|
+
}
|
|
80
|
+
const getter = (_a = (0, common_1.getPropertyDescriptor)(this.target, p)) === null || _a === void 0 ? void 0 : _a.get;
|
|
81
|
+
let value;
|
|
82
|
+
if (this.facade.propertyAccessorsAsWhiteBox && getter !== undefined && getter.origHadGetter !== false) { // Access via real property accessor ?
|
|
83
|
+
return value = getter.apply(this.proxy, []); // Call the accessor with a proxied this
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
//@ts-ignore
|
|
87
|
+
value = this.rawRead(p);
|
|
88
|
+
}
|
|
89
|
+
if (value != null && typeof value === "object") {
|
|
90
|
+
const descriptor = Object.getOwnPropertyDescriptor(this.target, p);
|
|
91
|
+
// Handle read-only property:
|
|
92
|
+
if (descriptor !== undefined && descriptor.writable === false) {
|
|
93
|
+
// The js runtime would prevent us from returning a proxy :( Pretty mean :(
|
|
94
|
+
throw new Error("Cannot proxy a read-only property. This is not implemented."); // TODO: Implement the virtual way (see constructor)
|
|
95
|
+
}
|
|
96
|
+
return this.facade.getProxyFor(value);
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
rawRead(key) {
|
|
101
|
+
//@ts-ignore
|
|
102
|
+
return this.target[key];
|
|
103
|
+
}
|
|
104
|
+
set(fake_target, p, value, receiver) {
|
|
105
|
+
var _a;
|
|
106
|
+
// Validity check
|
|
107
|
+
if (receiver !== this.proxy) {
|
|
108
|
+
throw new Error("Invalid state. Set was called on a different object than this proxy is for."); // Cannot imagine a legal case
|
|
109
|
+
}
|
|
110
|
+
const setter = (_a = (0, common_1.getPropertyDescriptor)(this.target, p)) === null || _a === void 0 ? void 0 : _a.set;
|
|
111
|
+
if (this.facade.propertyAccessorsAsWhiteBox && setter !== undefined && setter.origHadSetter !== false) { // Setting via real property accessor ?
|
|
112
|
+
setter.apply(this.proxy, [value]); // Only call the accessor with a proxied this
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const unproxiedValue = this.facade.getUnproxiedValue(value);
|
|
116
|
+
//@ts-ignore
|
|
117
|
+
if (this.target[p] !== unproxiedValue) { // modify ?
|
|
118
|
+
this.rawChange(p, unproxiedValue);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
rawChange(p, newUnproxiedValue) {
|
|
124
|
+
//@ts-ignore
|
|
125
|
+
this.target[p] = newUnproxiedValue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.FacadeProxyHandler = FacadeProxyHandler;
|
|
129
|
+
const proxyToProxyHandler = new WeakMap();
|
|
130
|
+
function getProxyHandler(proxy) {
|
|
131
|
+
return proxyToProxyHandler.get(proxy);
|
|
132
|
+
}
|
|
133
|
+
function isProxyForAFacade(obj) {
|
|
134
|
+
return proxyToProxyHandler.has(obj);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Makes the obj throw an error when trying to access it
|
|
138
|
+
* @param obj
|
|
139
|
+
* @param message
|
|
140
|
+
* @param cause
|
|
141
|
+
*/
|
|
142
|
+
function invalidateObject(obj, message, cause) {
|
|
143
|
+
const throwInvalid = () => {
|
|
144
|
+
throw new Error(message, { cause: cause });
|
|
145
|
+
};
|
|
146
|
+
// Delete all writeable own props:
|
|
147
|
+
const descrs = Object.getOwnPropertyDescriptors(obj);
|
|
148
|
+
for (const k in descrs) {
|
|
149
|
+
const desc = descrs[k];
|
|
150
|
+
if (desc.configurable) {
|
|
151
|
+
//@ts-ignore
|
|
152
|
+
delete obj[k];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
Object.setPrototypeOf(obj, new Proxy(obj, {
|
|
156
|
+
get(target, p, receiver) {
|
|
157
|
+
throwInvalid();
|
|
158
|
+
},
|
|
159
|
+
set(target, p, newValue, receiver) {
|
|
160
|
+
throwInvalid();
|
|
161
|
+
return false;
|
|
162
|
+
},
|
|
163
|
+
defineProperty(target, property, attributes) {
|
|
164
|
+
throwInvalid();
|
|
165
|
+
return false;
|
|
166
|
+
},
|
|
167
|
+
deleteProperty(target, p) {
|
|
168
|
+
throwInvalid();
|
|
169
|
+
return false;
|
|
170
|
+
},
|
|
171
|
+
ownKeys(target) {
|
|
172
|
+
throwInvalid();
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* @returns the real real origial object from the real world
|
|
179
|
+
*/
|
|
180
|
+
function getGlobalOrig(obj) {
|
|
181
|
+
let handler;
|
|
182
|
+
while ((handler = proxyToProxyHandler.get(obj)) !== undefined) {
|
|
183
|
+
obj = handler.target;
|
|
184
|
+
}
|
|
185
|
+
return obj;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=proxyFacade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxyFacade.js","sourceRoot":"","sources":["proxyFacade.ts"],"names":[],"mappings":"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"}
|
package/proxyFacade.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
getPropertyDescriptor,
|
|
6
|
+
GetterFlags,
|
|
7
|
+
objectMembershipInGraphs,
|
|
8
|
+
ObjKey,
|
|
9
|
+
PartialGraph,
|
|
10
|
+
SetterFlags
|
|
11
|
+
} from "./common";
|
|
12
|
+
import {deleteProperty} from "./origChangeTracking";
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export abstract class ProxyFacade<HANDLER extends FacadeProxyHandler<any>> extends PartialGraph {
|
|
16
|
+
// *** Configuration: ***
|
|
17
|
+
/**
|
|
18
|
+
* Treats them like functions, meaning, they get a proxied 'this'. WatchProxies will see the access to the real properties
|
|
19
|
+
*/
|
|
20
|
+
public propertyAccessorsAsWhiteBox = true;
|
|
21
|
+
|
|
22
|
+
// *** State: ***
|
|
23
|
+
protected objectsToProxyHandlers = new WeakMap<object, HANDLER>();
|
|
24
|
+
|
|
25
|
+
protected abstract crateHandler(target: object, facade: any): HANDLER;
|
|
26
|
+
|
|
27
|
+
getProxyFor<O>(value: O): O {
|
|
28
|
+
if(value === null || typeof value !== "object") { // not an object?
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if(this.hasObj(value)) { // Already our proxied object ?
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let handlerForObj = this.objectsToProxyHandlers.get(value);
|
|
37
|
+
if(handlerForObj !== undefined) { // value was an unproxied object and we have the proxy for it ?
|
|
38
|
+
return handlerForObj.proxy as O;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
handlerForObj = this.crateHandler(value, this);
|
|
42
|
+
// register:
|
|
43
|
+
proxyToProxyHandler.set(handlerForObj.proxy, handlerForObj);
|
|
44
|
+
this.objectsToProxyHandlers.set(value, handlerForObj);
|
|
45
|
+
this._register(handlerForObj.proxy);
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
return handlerForObj.proxy as O;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
*
|
|
53
|
+
* @param value
|
|
54
|
+
* @return the original non-proxied value
|
|
55
|
+
*/
|
|
56
|
+
getUnproxiedValue<O>(value: O): O {
|
|
57
|
+
if(value === null || typeof value !== "object") { // not an object?
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return proxyToProxyHandler.get(value)?.target as O|| value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getHandlerFor(obj: object) {
|
|
65
|
+
return getProxyHandler(this.getProxyFor(obj)) as HANDLER;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export abstract class FacadeProxyHandler<FACADE extends ProxyFacade<any>> implements ProxyHandler<object> {
|
|
71
|
+
target: object;
|
|
72
|
+
proxy: object;
|
|
73
|
+
facade: FACADE;
|
|
74
|
+
|
|
75
|
+
constructor(target: object, facade: FACADE) {
|
|
76
|
+
this.target = target;
|
|
77
|
+
this.facade = facade;
|
|
78
|
+
|
|
79
|
+
// Create proxy:
|
|
80
|
+
//const targetForProxy = {}; // The virtual way
|
|
81
|
+
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
|
|
82
|
+
this.proxy = new Proxy(targetForProxy, this);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
deleteProperty(target: object, key: string | symbol): boolean {
|
|
86
|
+
//@ts-ignore
|
|
87
|
+
return deleteProperty(this.target,key);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
defineProperty(target: object, property: string | symbol, attributes: PropertyDescriptor): boolean {
|
|
91
|
+
throw new Error("Must not use defineProperty on a proxied object. Handling of change tracking etc. for this may not be implemented");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get (fake_target:object, p:string | symbol, receiver:any) {
|
|
95
|
+
// Validity check
|
|
96
|
+
if(receiver !== this.proxy) {
|
|
97
|
+
throw new Error("Invalid state. Get was called on a different object than this proxy is for."); // Cannot imagine a legal case
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const getter = getPropertyDescriptor(this.target, p)?.get;
|
|
101
|
+
let value;
|
|
102
|
+
if(this.facade.propertyAccessorsAsWhiteBox && getter !== undefined && (getter as GetterFlags).origHadGetter !== false) { // Access via real property accessor ?
|
|
103
|
+
return value = getter.apply(this.proxy,[]); // Call the accessor with a proxied this
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
//@ts-ignore
|
|
107
|
+
value = this.rawRead(p);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if(value != null && typeof value === "object") {
|
|
111
|
+
const descriptor = Object.getOwnPropertyDescriptor(this.target, p);
|
|
112
|
+
|
|
113
|
+
// Handle read-only property:
|
|
114
|
+
if(descriptor !== undefined && descriptor.writable === false) {
|
|
115
|
+
// The js runtime would prevent us from returning a proxy :( Pretty mean :(
|
|
116
|
+
throw new Error("Cannot proxy a read-only property. This is not implemented."); // TODO: Implement the virtual way (see constructor)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return this.facade.getProxyFor(value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
protected rawRead(key: ObjKey): unknown {
|
|
126
|
+
//@ts-ignore
|
|
127
|
+
return this.target[key as any];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
set(fake_target:object, p:string | symbol, value:any, receiver:any) {
|
|
131
|
+
// Validity check
|
|
132
|
+
if(receiver !== this.proxy) {
|
|
133
|
+
throw new Error("Invalid state. Set was called on a different object than this proxy is for."); // Cannot imagine a legal case
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const setter = getPropertyDescriptor(this.target, p)?.set;
|
|
137
|
+
if(this.facade.propertyAccessorsAsWhiteBox && setter !== undefined && (setter as SetterFlags).origHadSetter !== false) { // Setting via real property accessor ?
|
|
138
|
+
setter.apply(this.proxy,[value]); // Only call the accessor with a proxied this
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
const unproxiedValue = this.facade.getUnproxiedValue(value);
|
|
142
|
+
//@ts-ignore
|
|
143
|
+
if (this.target[p] !== unproxiedValue) { // modify ?
|
|
144
|
+
this.rawChange(p, unproxiedValue);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return true
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
protected rawChange(p: string | symbol, newUnproxiedValue: any) {
|
|
151
|
+
//@ts-ignore
|
|
152
|
+
this.target[p] = newUnproxiedValue
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
const proxyToProxyHandler = new WeakMap<object, FacadeProxyHandler<any>>();
|
|
161
|
+
function getProxyHandler(proxy: object) {
|
|
162
|
+
return proxyToProxyHandler.get(proxy);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function isProxyForAFacade(obj: object) {
|
|
166
|
+
return proxyToProxyHandler.has(obj);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Makes the obj throw an error when trying to access it
|
|
171
|
+
* @param obj
|
|
172
|
+
* @param message
|
|
173
|
+
* @param cause
|
|
174
|
+
*/
|
|
175
|
+
export function invalidateObject(obj: object, message: string, cause?: Error) {
|
|
176
|
+
const throwInvalid = () => {
|
|
177
|
+
throw new Error(message, {cause: cause});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Delete all writeable own props:
|
|
181
|
+
const descrs = Object.getOwnPropertyDescriptors(obj);
|
|
182
|
+
for(const k in descrs) {
|
|
183
|
+
const desc = descrs[k];
|
|
184
|
+
if(desc.configurable) {
|
|
185
|
+
//@ts-ignore
|
|
186
|
+
delete obj[k];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
Object.setPrototypeOf(obj, new Proxy(obj, {
|
|
191
|
+
get(target: object, p: string | symbol, receiver: any): any {
|
|
192
|
+
throwInvalid();
|
|
193
|
+
},
|
|
194
|
+
set(target: object, p: string | symbol, newValue: any, receiver: any): boolean {
|
|
195
|
+
throwInvalid()
|
|
196
|
+
return false;
|
|
197
|
+
},
|
|
198
|
+
defineProperty(target: object, property: string | symbol, attributes: PropertyDescriptor): boolean {
|
|
199
|
+
throwInvalid();
|
|
200
|
+
return false;
|
|
201
|
+
},
|
|
202
|
+
deleteProperty(target: object, p: string | symbol): boolean {
|
|
203
|
+
throwInvalid()
|
|
204
|
+
return false;
|
|
205
|
+
},
|
|
206
|
+
ownKeys(target: object): ArrayLike<string | symbol> {
|
|
207
|
+
throwInvalid()
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
}))
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @returns the real real origial object from the real world
|
|
215
|
+
*/
|
|
216
|
+
export function getGlobalOrig<T extends object>(obj: T): T {
|
|
217
|
+
let handler: FacadeProxyHandler<any> | undefined
|
|
218
|
+
while((handler = proxyToProxyHandler.get(obj)) !== undefined) {
|
|
219
|
+
obj = handler.target as T;
|
|
220
|
+
}
|
|
221
|
+
return obj;
|
|
222
|
+
}
|