link-stack/packages/montar/src/proxy.ts

126 lines
3.3 KiB
TypeScript
Raw Normal View History

2023-03-13 10:04:22 +00:00
/* eslint-disable no-new,no-useless-call */
// mutableProxyFactory from https://stackoverflow.com/a/54460544
// (C) Alex Hall https://stackoverflow.com/users/2482744/alex-hall
// License CC BY-SA 3.0
/* eslint-disable @typescript-eslint/ban-types */
export class PProxyHandler<T extends object> implements ProxyHandler<T> {
getPrototypeOf?(target: T): object | null {
return Reflect.getPrototypeOf(target);
}
setPrototypeOf?(target: T, v: any): boolean {
return Reflect.setPrototypeOf(target, v);
}
isExtensible?(target: T): boolean {
return Reflect.isExtensible(target);
}
preventExtensions?(target: T): boolean {
return Reflect.preventExtensions(target);
}
getOwnPropertyDescriptor?(
target: T,
p: PropertyKey
): PropertyDescriptor | undefined {
return Reflect.getOwnPropertyDescriptor(target, p);
}
has?(target: T, p: PropertyKey): boolean {
return Reflect.has(target, p);
}
get?(target: T, p: PropertyKey, receiver: any): any {
return Reflect.get(target, p, receiver);
}
set?(target: T, p: PropertyKey, value: any, receiver: any): boolean {
return Reflect.set(target, p, value, receiver);
}
deleteProperty?(target: T, p: PropertyKey): boolean {
return Reflect.deleteProperty(target, p);
}
defineProperty?(
target: T,
p: PropertyKey,
attributes: PropertyDescriptor
): boolean {
return Reflect.defineProperty(target, p, attributes);
}
enumerate?(target: T): PropertyKey[] {
return Reflect.ownKeys(target);
}
2023-05-26 08:27:16 +00:00
// @ts-expect-error
2023-03-13 10:04:22 +00:00
ownKeys?(target: T): PropertyKey[] {
return Reflect.ownKeys(target);
}
apply?(target: T, thisArg: any, argArray?: any): any {
return Reflect.apply(target as Function, thisArg, argArray);
}
construct?(target: T, argArray: any, newTarget?: any): object {
return Reflect.construct(target as Function, argArray, newTarget);
}
}
interface MutableProxy<T extends object> {
setTarget(target: T): void;
setHandler(handler: PProxyHandler<T>): void;
getTarget(): T;
getHandler(): ProxyHandler<T>;
proxy: T;
}
export function mutableProxyFactory<T extends object>(
mutableTarget: T,
mutableHandler?: ProxyHandler<T>
): MutableProxy<T> {
2023-05-26 08:27:16 +00:00
if (!mutableHandler) mutableHandler = new PProxyHandler() as any;
2023-03-13 10:04:22 +00:00
return {
setTarget(target: T): void {
new Proxy(target, {}); // test target validity
mutableTarget = target;
},
setHandler(handler: PProxyHandler<T>): void {
2023-05-26 08:27:16 +00:00
new Proxy({}, handler as any); // test handler validity
2023-03-13 10:04:22 +00:00
Object.keys(handler).forEach((key) => {
const value = handler[key];
if (Reflect[key] && typeof value !== "function") {
throw new Error(`Trap "${key}: ${value}" is not a function`);
}
});
2023-05-26 08:27:16 +00:00
mutableHandler = handler as any;
2023-03-13 10:04:22 +00:00
},
getTarget(): T {
return mutableTarget;
},
2023-05-26 08:27:16 +00:00
// @ts-expect-error
2023-03-13 10:04:22 +00:00
getHandler(): PProxyHandler<T> {
2023-05-26 08:27:16 +00:00
return mutableHandler as any;
2023-03-13 10:04:22 +00:00
},
proxy: new Proxy(
mutableTarget,
new Proxy(
{},
{
// Dynamically forward all the traps to the associated methods on the mutable handler
get(target, property) {
return (_target, ...args) =>
mutableHandler[property].apply(mutableHandler, [
mutableTarget,
...args,
]);
},
}
)
),
};
}