virtual-proxy
Advanced tools
+381
| const { | ||
| Proxy, | ||
| Proxy: { revocable }, | ||
| String, | ||
| JSON: { stringify }, | ||
| Object: { hasOwn }, | ||
| TypeError, | ||
| Reflect: { | ||
| isExtensible, | ||
| defineProperty, | ||
| deleteProperty, | ||
| ownKeys, | ||
| setPrototypeOf, | ||
| preventExtensions, | ||
| }, | ||
| } = globalThis; | ||
| ////////// | ||
| // Util // | ||
| ////////// | ||
| /** | ||
| * @type {<X>(array: X[], item: X) => boolean} | ||
| */ | ||
| const includes = (array, item) => { | ||
| const { length } = array; | ||
| for (let index = 0; index < length; index++) { | ||
| if (array[index] === item) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| }; | ||
| //////////////////// | ||
| // VirtualHandler // | ||
| //////////////////// | ||
| /** | ||
| * @type {( | ||
| * handler: keyof typeof Reflect, | ||
| * operation: keyof typeof Reflect, | ||
| * ) => string} | ||
| */ | ||
| const format = (operation, handler) => | ||
| `Cannot perform ${operation} on virtual target in ${handler} handler`; | ||
| /** | ||
| * @type {( | ||
| * handler: keyof typeof Reflect, | ||
| * operation: keyof typeof Reflect, | ||
| * key: string | symbol, | ||
| * ) => string} | ||
| */ | ||
| const formatWithKey = (handler, operation, key) => | ||
| `Cannot perform ${operation} (${stringify(String(key))}) on virtual target in ${handler} handler`; | ||
| /** | ||
| * @type {<V extends object, T extends object>( | ||
| * origin: "preventExtensions" | "isExtensible", | ||
| * $target: T, | ||
| * target: V, | ||
| * handler: import(".").VirtualHandler<V>, | ||
| * ) => boolean} | ||
| */ | ||
| const preventExtensionHelper = (origin, $target, target, handler) => { | ||
| const keys = handler.ownKeys(target); | ||
| for (let index = 0, length = keys.length; index < length; index++) { | ||
| const key = keys[index]; | ||
| if (!hasOwn($target, key)) { | ||
| if ( | ||
| !defineProperty($target, key, { | ||
| value: null, | ||
| writable: true, | ||
| configurable: true, | ||
| enumerable: true, | ||
| }) | ||
| ) { | ||
| throw new TypeError(formatWithKey(origin, "defineProperty", key)); | ||
| } | ||
| } | ||
| } | ||
| if (!setPrototypeOf($target, handler.getPrototypeOf(target))) { | ||
| throw new TypeError(format(origin, "setPrototypeOf")); | ||
| } | ||
| return preventExtensions($target); | ||
| }; | ||
| /** | ||
| * @template {object} V | ||
| * @template {object} T | ||
| * @type {import(".").ActualHandler<V, T>} | ||
| */ | ||
| const actual_handler_prototype = { | ||
| ////////////// | ||
| // Function // | ||
| ////////////// | ||
| // a) [RELEGATED] The target must be a callable itself. That is, it must be a function object. | ||
| apply(_$target, that, args) { | ||
| const { handler, target } = this; | ||
| return handler.apply(target, that, args); | ||
| }, | ||
| // a) [RELEGATED] The target must be a constructor itself. | ||
| // b) [RELEGATED] The result must be an Object. | ||
| construct(_$target, args, new_target) { | ||
| const { handler, target } = this; | ||
| // construct(Boolean, [], /** @type {Function} */ ($target)); | ||
| // construct(Boolean, [], /** @type {Function} */ (new_target)); | ||
| return handler.construct(target, args, new_target); | ||
| }, | ||
| //////////////// | ||
| // Extensible // | ||
| //////////////// | ||
| // a) The result must be the same as Reflect.isExtensible() on the target object. | ||
| isExtensible($target) { | ||
| const { handler, target } = this; | ||
| const extensible = handler.isExtensible(target); | ||
| // a) | ||
| if (extensible && !isExtensible($target)) { | ||
| throw new TypeError("Cannot undo preventExtensions on virtual target"); | ||
| } | ||
| if (!extensible && isExtensible($target)) { | ||
| /* c8 ignore start */ | ||
| if (!preventExtensionHelper("isExtensible", $target, target, handler)) { | ||
| throw new TypeError(format("isExtensible", "preventExtensions")); | ||
| } | ||
| /* c8 ignore stop */ | ||
| } | ||
| return extensible; | ||
| }, | ||
| // a) The result is only true if Reflect.isExtensible() on the target object returns false after calling handler.preventExtensions() | ||
| preventExtensions($target) { | ||
| const { handler, target } = this; | ||
| const success = handler.preventExtensions(target); | ||
| // a) | ||
| if (success) { | ||
| /* c8 ignore start */ | ||
| if ( | ||
| !preventExtensionHelper("preventExtensions", $target, target, handler) | ||
| ) { | ||
| throw new TypeError(format("preventExtensions", "preventExtensions")); | ||
| } | ||
| /* c8 ignore stop */ | ||
| } | ||
| return success; | ||
| }, | ||
| /////////////// | ||
| // Prototype // | ||
| /////////////// | ||
| // a) [RELEGATED] The result must be either an Object or null | ||
| // b) [RELEGATED] If the target object is not extensible, the result must be the same as the result of Reflect.getPrototypeOf(target). | ||
| getPrototypeOf(_$target) { | ||
| const { handler, target } = this; | ||
| return handler.getPrototypeOf(target); | ||
| }, | ||
| // a) [RELEGATED] If the target object is not extensible, the prototype cannot be changed. | ||
| setPrototypeOf(_$target, prototype) { | ||
| const { handler, target } = this; | ||
| return handler.setPrototypeOf(target, prototype); | ||
| }, | ||
| ////////////// | ||
| // Property // | ||
| ////////////// | ||
| // a) [RELEGATED] A property cannot be added, if the target object is not extensible. | ||
| // b) A property cannot be non-configurable, unless there exists a corresponding non-configurable own property of the target object. | ||
| // c) A non-configurable property cannot be non-writable, unless there exists a corresponding non-configurable, non-writable own property of the target object. | ||
| // d) If a property has a corresponding property on the target object, then the target object property's descriptor must be compatible with descriptor. | ||
| defineProperty($target, key, descriptor) { | ||
| const { handler, target } = this; | ||
| // b) + c) + d) | ||
| const success = handler.defineProperty(target, key, descriptor); | ||
| if (success && !descriptor.configurable) { | ||
| if (!defineProperty($target, key, descriptor)) { | ||
| throw new TypeError( | ||
| formatWithKey("defineProperty", "defineProperty", key), | ||
| ); | ||
| } | ||
| } | ||
| return success; | ||
| }, | ||
| // a) [RELEGATED] The result must be either an Object or undefined. | ||
| // b) [RELEGATED] A property cannot be reported as non-existent, if it exists as a non-configurable own property of the target object. | ||
| // c) A property cannot be reported as non-existent, if it exists as an own property of a non-extensible target object. | ||
| // d) [RELEGATED] A property cannot be reported as existent, if it does not exist as an own property of the target object and the target object is not extensible. | ||
| // e) A property cannot be reported as non-configurable, unless it exists as a non-configurable own property of the target object. | ||
| // f) A property cannot be reported as both non-configurable and non-writable, unless it exists as a non-configurable, non-writable own property of the target object. | ||
| // g) If a property has a corresponding property on the target object, then the target object property's descriptor must be compatible with descriptor. | ||
| getOwnPropertyDescriptor($target, key) { | ||
| const { handler, target } = this; | ||
| const descriptor = handler.getOwnPropertyDescriptor(target, key); | ||
| // c) | ||
| if (!descriptor && !isExtensible($target)) { | ||
| if (!deleteProperty($target, key)) { | ||
| throw new TypeError( | ||
| formatWithKey("getOwnPropertyDescriptor", "deleteProperty", key), | ||
| ); | ||
| } | ||
| } | ||
| // e) + f) + g) | ||
| if (descriptor && !descriptor.configurable) { | ||
| if (!defineProperty($target, key, descriptor)) { | ||
| throw new TypeError( | ||
| formatWithKey("getOwnPropertyDescriptor", "defineProperty", key), | ||
| ); | ||
| } | ||
| } | ||
| return descriptor; | ||
| }, | ||
| // a) [RELEGATED] A property cannot be reported as deleted, if it exists as a non-configurable own property of the target object. | ||
| // b) A property cannot be reported as deleted, if it exists as an own property of the target object and the target object is non-extensible. | ||
| deleteProperty($target, key) { | ||
| const { handler, target } = this; | ||
| const success = handler.deleteProperty(target, key); | ||
| // b) | ||
| if (success && !isExtensible($target)) { | ||
| if (!deleteProperty($target, key)) { | ||
| throw new TypeError( | ||
| formatWithKey("deleteProperty", "deleteProperty", key), | ||
| ); | ||
| } | ||
| } | ||
| return success; | ||
| }, | ||
| // a) [RELEGATED] The result is an Object. | ||
| // b) [RELEGATED] The list of keys contains no duplicate values. | ||
| // c) [RELEGATED] The type of each key is either a String or a Symbol. | ||
| // d) [RELEGATED] The type of each array element is either a String or a Symbol. | ||
| // e) [RELEGATED] The result list must contain the keys of all non-configurable own properties of the target object. | ||
| // f) If the target object is not extensible, then the result list must contain all the keys of the own properties of the target object and no other values. | ||
| ownKeys($target) { | ||
| const { handler, target } = this; | ||
| const keys = handler.ownKeys(target); | ||
| // f) | ||
| if (!isExtensible($target)) { | ||
| const $keys = ownKeys($target); | ||
| const $length = $keys.length; | ||
| for (let $index = 0; $index < $length; $index++) { | ||
| const $key = $keys[$index]; | ||
| if (!includes(keys, $key)) { | ||
| if (!deleteProperty($target, $key)) { | ||
| throw new TypeError( | ||
| formatWithKey("ownKeys", "deleteProperty", $key), | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return keys; | ||
| }, | ||
| ////////////////////// | ||
| // Property-Derived // | ||
| ////////////////////// | ||
| // a) [RELEGATED] A property cannot be reported as non-existent, if it exists as a non-configurable own property of the target object. | ||
| // b) A property cannot be reported as non-existent, if it exists as an own property of the target object and the target object is not extensible. | ||
| has($target, key) { | ||
| const { handler, target } = this; | ||
| const existent = handler.has(target, key); | ||
| // b) | ||
| if (!existent && !isExtensible($target)) { | ||
| if (!deleteProperty($target, key)) { | ||
| throw new TypeError(formatWithKey("has", "deleteProperty", key)); | ||
| } | ||
| } | ||
| return existent; | ||
| }, | ||
| // a) [RELEGATED] The value reported for a property must be the same as the value of the corresponding target object property, if the target object property is a non-writable, non-configurable own data property. | ||
| // b) [RELEGATED] The value reported for a property must be undefined, if the corresponding target object property is a non-configurable own accessor property that has an undefined getter. | ||
| get(_$target, key, receiver) { | ||
| const { handler, target } = this; | ||
| return handler.get(target, key, receiver); | ||
| }, | ||
| // a) [RELEGATED] Cannot change the value of a property to be different from the value of the corresponding target object property, if the corresponding target object property is a non-writable, non-configurable own data property. | ||
| // b) [RELEGATED] Cannot set the value of a property if the corresponding target object property is a non-configurable own accessor property that has an undefined setter. | ||
| set(_$target, key, value, receiver) { | ||
| const { handler, target } = this; | ||
| return handler.set(target, key, value, receiver); | ||
| }, | ||
| }; | ||
| //////////// | ||
| // Export // | ||
| //////////// | ||
| const reflection = /** @type {(keyof typeof Reflect)[]} */ (ownKeys(Reflect)); | ||
| /** | ||
| * @type {<V extends object>( | ||
| * handler: ProxyHandler<V>, | ||
| * ) => import(".").VirtualHandler<V>} | ||
| */ | ||
| const toVirtualHandler = (handler) => { | ||
| const descriptor = { | ||
| __proto__: null, | ||
| value: /** @type {any} */ (null), | ||
| writable: true, | ||
| enumerable: true, | ||
| configurable: true, | ||
| }; | ||
| const { length } = reflection; | ||
| for (let index = 0; index < length; index++) { | ||
| const key = reflection[index]; | ||
| if (!hasOwn(handler, key)) { | ||
| descriptor.value = Reflect[key]; | ||
| defineProperty(handler, key, descriptor); | ||
| } | ||
| } | ||
| return /** @type {any} */ (handler); | ||
| }; | ||
| /** | ||
| * @type {<V extends object, T extends object>( | ||
| * target: V, | ||
| * handler: ProxyHandler<V>, | ||
| * ) => import(".").ActualHandler<V, T>} | ||
| */ | ||
| export const setupVirtualHandler = (target, handler) => | ||
| /** @type {any} */ ({ | ||
| __proto__: actual_handler_prototype, | ||
| target, | ||
| handler: toVirtualHandler(handler), | ||
| }); | ||
| /** | ||
| * @type {<V extends object, T extends object>( | ||
| * integrity: T, | ||
| * target: V, | ||
| * handler: ProxyHandler<V>, | ||
| * ) => T} | ||
| */ | ||
| export const VirtualProxy = function (integrity, target, handler) { | ||
| return new Proxy(integrity, setupVirtualHandler(target, handler)); | ||
| }; | ||
| /** | ||
| * @type {<V extends object, T extends object>( | ||
| * integrity: T, | ||
| * target: V, | ||
| * handler: ProxyHandler<V>, | ||
| * ) => { proxy: T, revoke: () => void }} | ||
| */ | ||
| export const RevocableVirtualProxy = function (integrity, target, handler) { | ||
| return revocable(integrity, setupVirtualHandler(target, handler)); | ||
| }; | ||
| /** | ||
| * @type {<V extends object>( | ||
| * target: V, | ||
| * handler: ProxyHandler<V>, | ||
| * ) => object} | ||
| */ | ||
| export const VirtualObject = function (virtual, handler) { | ||
| return VirtualProxy({ __proto__: null }, virtual, handler); | ||
| }; | ||
| /** | ||
| * @type {<V extends object>( | ||
| * virtual: V, | ||
| * handler: ProxyHandler<V>, | ||
| * ) => any[]} | ||
| */ | ||
| export const VirtualArray = function (virtual, handler) { | ||
| return VirtualProxy([], virtual, handler); | ||
| }; | ||
| /** | ||
| * @type {<V extends object>( | ||
| * virtual: V, | ||
| * handler: ProxyHandler<V>, | ||
| * ) => Function} | ||
| */ | ||
| export const VirtualFunction = function (virtual, handler) { | ||
| return VirtualProxy( | ||
| /* c8 ignore start */ | ||
| function () { | ||
| "use-strict"; | ||
| }, | ||
| /* c8 ignore stop */ | ||
| virtual, | ||
| handler, | ||
| ); | ||
| }; |
+83
| /** | ||
| * Main export of the library, it creates an handler object that should be | ||
| * passed to the Proxy constructor. | ||
| * | ||
| * ```js | ||
| * const proxy = new Proxy(integrity, setupVirtualHandler(target, handler)); | ||
| * ``` | ||
| * | ||
| * @param target The virtual target object that will be passed to the virtual | ||
| * handler functions. | ||
| * @param handler The virtual handler object which has the same interface as | ||
| * a regular proxy handler object. | ||
| * @returns An (actual) handler object that should be passed to the proxy | ||
| * constructor. | ||
| */ | ||
| export type setupVirtualHandler = <V extends object, T extends object>( | ||
| target: V, | ||
| handler: ProxyHandler<V>, | ||
| ) => ProxyHandler<T>; | ||
| /** | ||
| * Cosmetic function to create a virtual proxy object, that still enforces MOP | ||
| * invariants, but independently from the virtual target object. | ||
| * @param integrity The actual target object of the proxy that is used to track | ||
| * information for enforcing MOP invariants. | ||
| * @param target The virtual target object that will be passed to the virtual | ||
| * handler object. | ||
| * @param handler The virtual handler object which has the same interface as | ||
| * a regular proxy handler object. | ||
| * @returns A virtual proxy object. | ||
| */ | ||
| export type VirtualProxy = new <V extends object, T extends object>( | ||
| integrity: T, | ||
| target: V, | ||
| handler: ProxyHandler<V>, | ||
| ) => T; | ||
| /** | ||
| * Cosmetic function to create a revocable virtual proxy object, similar to | ||
| * VirtualProxy but it also returns a function to revoke the proxy. | ||
| * @param integrity The actual target object. | ||
| * @param target The virtual target object. | ||
| * @param handler The virtual handler object. | ||
| * @returns Both a virtual proxy object and a function to revoke the proxy. | ||
| */ | ||
| export type RevocableVirtualProxy = new <V extends object, T extends object>( | ||
| integrity: T, | ||
| target: V, | ||
| handler: ProxyHandler<V>, | ||
| ) => { proxy: T; revoke: () => void }; | ||
| /** | ||
| * Cosmetic function to create a plain virtual proxy object. | ||
| * @param target The virtual target object. | ||
| * @param handler The virtual handler object. | ||
| * @returns A plain virtual proxy object. | ||
| */ | ||
| export type VirtualObject = new <V extends object, T extends object>( | ||
| target: V, | ||
| handler: ProxyHandler<V>, | ||
| ) => T; | ||
| /** | ||
| * Cosmetic function to create a virtual proxy array. | ||
| * @param target The virtual target object. | ||
| * @param handler The virtual handler object. | ||
| * @returns A virtual proxy array. | ||
| */ | ||
| export type VirtualArray = new <V extends object, T extends any[]>( | ||
| target: V, | ||
| handler: ProxyHandler<V>, | ||
| ) => T; | ||
| /** | ||
| * Cosmetic function to create a virtual proxy function. | ||
| * @param target The virtual target object. | ||
| * @param handler The virtual handler object. | ||
| * @returns A virtual proxy function. | ||
| */ | ||
| export type VirtualFunction = new <V extends object, T extends Function>( | ||
| target: V, | ||
| handler: ProxyHandler<V>, | ||
| ) => T; |
+19
-3
| { | ||
| "name": "virtual-proxy", | ||
| "description": "Create ECMAScript6 with a virtual target", | ||
| "version": "0.0.11", | ||
| "version": "0.1.0", | ||
| "author": { | ||
@@ -9,5 +9,15 @@ "name": "Laurent Christophe", | ||
| }, | ||
| "files": [ | ||
| "lib/index.mjs", | ||
| "type.d.ts" | ||
| ], | ||
| "types": "type.d.ts", | ||
| "type": "module", | ||
| "main": "main.js", | ||
| "repository": "lachrist/virtual-proxy", | ||
| "homepage": "http://github.com/lachrist/virtual-proxy", | ||
| "scripts": { | ||
| "test": "node test/test.mjs", | ||
| "test-cov": "npx c8 -- node test/test.mjs" | ||
| }, | ||
| "license": "MIT", | ||
@@ -18,3 +28,9 @@ "keywords": [ | ||
| "Invariants" | ||
| ] | ||
| } | ||
| ], | ||
| "devDependencies": { | ||
| "@types/node": "^22.10.2", | ||
| "c8": "^10.1.3", | ||
| "prettier": "^3.4.2", | ||
| "typescript": "^5.7.2" | ||
| } | ||
| } |
+42
-64
| # VirtualProxy | ||
| <!-- | ||
| const forward = (name) => function () { | ||
| return name in this.handler ? | ||
| this.handler[name](...arguments) : | ||
| Reflect[name](...arguments); | ||
| }; | ||
| The Proxy API enables powerful reflection on JavaScript objects. However, it is | ||
| constrained by strict invariants, such as requiring proxies to report consistent | ||
| values for immutable properties (i.e., non-configurable and non-writable). While | ||
| these invariants are essential for ensuring the consistent behavior of | ||
| JavaScript objects, the current implementation imposes unnecessary limitations | ||
| by using the target object for bookkeeping. | ||
| const dispatch = (name, handler, array) => name in handler ? | ||
| handler[name](...array) : | ||
| Reflect[name](...array); | ||
| This module introduces a mechanism to decouple the target object from the | ||
| bookkeeping process, enabling more flexible and versatile use of proxies. | ||
| const synchronize = (self, shadow) => { | ||
| if (Reflect.isExtensible(shadow)) { | ||
| Reflect.setPrototypeOf(shadow, dispatch("getPrototypeOf", self.handler, [self.target])); | ||
| const keys = Reflect.ownKeys(shadow); | ||
| for (let key of dispatch("ownKeys", self.handler, [self.target])) { | ||
| if (!keys.includes(key)) { | ||
| Reflect.defineProperty(shadow, key, {configurable:true}); | ||
| } | ||
| } | ||
| Reflect.preventExtensions(shadow); | ||
| } | ||
| }; | ||
| ## Demo | ||
| const ShadowHandlerPrototype = { | ||
| apply: forward("apply"), | ||
| construct: forward("construct"), | ||
| getPrototypeOf: forward("getPrototypeOf"), | ||
| setPrototypeOf: forward("setPrototypeOf"), | ||
| get: forward("get"), | ||
| set: forward("set"), | ||
| has: forward("has"), | ||
| ownKeys: forward("ownKeys"), | ||
| deleteProperty: forward("deleteProperty"), | ||
| defineProperty: function (shadow, key, descriptor) { | ||
| descriptor = Object.assign({}, descriptor); | ||
| const result = dispatch("defineProperty", this.handler, [this.target, key, descriptor]); | ||
| if (result && !descriptor.configurable) | ||
| Reflect.defineProperty(shadow, key, descriptor); | ||
| return result; | ||
| }, | ||
| getOwnPropertyDescriptor: function (shadow, key) { | ||
| const result = dispatch("getOwnPropertyDescriptor", this.handler, [this.target, key]) | ||
| if (!result.configurable) | ||
| Reflect.defineProperty(shadow, key, result); | ||
| return result; | ||
| }, | ||
| preventExtensions: function (shadow) { | ||
| const result = dispatch("preventExtensions", this.handler, [this.target]); | ||
| if (result) | ||
| synchronize(this, shadow); | ||
| return result; | ||
| }, | ||
| isExtensible: function (shadow) { | ||
| const result = dispatch("isExtensible", this.handler, [this.target]); | ||
| if (!result) | ||
| synchronize(this, shadow); | ||
| return result; | ||
| } | ||
| ```js | ||
| import { VirtualProxy, setupVirtualHandler } from "../lib/index.mjs"; | ||
| const target = Object.freeze({ | ||
| name: "John", | ||
| password: "1234", | ||
| }); | ||
| const handler = { | ||
| get: (target, key, receiver) => | ||
| key === "password" ? "****" : Reflect.get(target, key, receiver), | ||
| }; | ||
| // 1) Proxy | ||
| { | ||
| // TypeError: 'get' on proxy: property 'password' is a read-only and | ||
| // non-configurable data property on the proxy target but the proxy | ||
| // did not return its actual value | ||
| const proxy = new Proxy(target, handler); | ||
| console.log(proxy.password); | ||
| } | ||
| // 2) VirtualProxy | ||
| { | ||
| const integrity = {}; | ||
| const proxy = new VirtualProxy(integrity, target, handler); | ||
| console.log(proxy.password); // **** | ||
| } | ||
| // 3) setupVirtualHandler | ||
| { | ||
| const integrity = {}; | ||
| const proxy = new Proxy(integrity, setupVirtualHandler(target, handler)); | ||
| console.log(proxy.password); // **** | ||
| } | ||
| ``` | ||
| function ShadowProxy (shadow, target, handler) { | ||
| if (!new.target) | ||
| throw new Error("Constructor ShadowProxy requires 'new'"); | ||
| const shadowHandler = Object.create(ShadowHandlerPrototype); | ||
| shadowHandler.handler = handler; | ||
| shadowHandler.target = target; | ||
| return new Proxy(shadow, shadowHandler); | ||
| }; --> | ||
| ## API | ||
| [TypeScript Declaration](./type.d.ts) |
-306
| const Proxy = global.Proxy; | ||
| const Boolean = global.Boolean; | ||
| const TypeError = global.TypeError; | ||
| const Reflect_apply = Reflect.apply; | ||
| const Reflect_construct = Reflect.construct; | ||
| const Reflect_defineProperty = Reflect.defineProperty; | ||
| const Reflect_deleteProperty = Reflect.deleteProperty; | ||
| const Reflect_get = Reflect.get; | ||
| const Reflect_getOwnPropertyDescriptor = Reflect.getOwnPropertyDescriptor; | ||
| const Reflect_getPrototypeOf = Reflect.getPrototypeOf; | ||
| const Reflect_has = Reflect.has; | ||
| const Reflect_isExtensible = Reflect.isExtensible; | ||
| const Reflect_preventExtensions = Reflect.preventExtensions; | ||
| const Reflect_ownKeys = Reflect.ownKeys; | ||
| const Reflect_set = Reflect.set; | ||
| const Reflect_setPrototypeOf = Reflect.setPrototypeOf; | ||
| // https://tc39.github.io/ecma262/#sec-invariants-of-the-essential-internal-methods | ||
| const handler = {__proto__:null}; | ||
| ////////////// | ||
| // Function // | ||
| ////////////// | ||
| // The target must be a callable itself. That is, it must be a function object. | ||
| handler.apply = function (target, value, values) { | ||
| if (typeof target !== "function") | ||
| throw new TypeError("Target is not a function"); | ||
| return this.__handler__.apply(this.__target__, value, values); | ||
| }; | ||
| // [RELEGATED] The result must be an Object. | ||
| handler.construct = function (target, values, newtarget) { | ||
| Reflect.construct(Boolean, [], target); | ||
| Reflect.construct(Boolean, [], newtarget); | ||
| return this.__handler__.construct(this.__target__, values, newtarget); | ||
| }; | ||
| ////////////// | ||
| // Property // | ||
| ////////////// | ||
| // https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc | ||
| // A property cannot be added, if the target object is not extensible. | ||
| // A property cannot be added as or modified to be non-configurable, if it does not exists as a non-configurable own property of the target object. | ||
| // A property may not be non-configurable, if a corresponding configurable property of the target object exists. | ||
| // If a property has a corresponding target object property then Object.defineProperty(target, prop, descriptor) will not throw an exception. | ||
| // In strict mode, a false return value from the defineProperty handler will throw a TypeError exception. | ||
| handler.defineProperty = function (target, key, descriptor_argument) { | ||
| const descriptor = Reflect_getOwnPropertyDescriptor(target, key); | ||
| if (!descriptor && !Reflect_isExtensible(target)) | ||
| return false; | ||
| if (descriptor && !descriptor.configurable) { | ||
| if (descriptor_argument.configurable) | ||
| return false; | ||
| if (descriptor.enumerable && !descriptor_argument.enumerable) | ||
| return false; | ||
| if (!descriptor.enumerable && descriptor_argument.enumerable) | ||
| return false; | ||
| if (Reflect_getOwnPropertyDescriptor(descriptor_argument, "value") || Reflect_getOwnPropertyDescriptor(descriptor_argument, "writable")) { | ||
| if (Reflect_getOwnPropertyDescriptor(descriptor, "get")) | ||
| return false; | ||
| if (!descriptor.writable && descriptor.writable) | ||
| return false; | ||
| if (!descriptor.writable && Reflect_getOwnPropertyDescriptor(descriptor_argument, "value") && descriptor.value !== descriptor_argument.value) | ||
| return false; | ||
| } else { | ||
| if (Reflect_getOwnPropertyDescriptor(descriptor, "value")) | ||
| return false; | ||
| if (Reflect_getOwnPropertyDescriptor(descriptor_argument, "get") && descriptor.get !== descriptor_argument.get) | ||
| return false; | ||
| if (Reflect_getOwnPropertyDescriptor(descriptor_argument, "set") && descriptor.set !== descriptor_argument.set) | ||
| return false; | ||
| } | ||
| } | ||
| if (!descriptor_argument.configurable) | ||
| Reflect_defineProperty(target, key, descriptor_argument); | ||
| return this.__handler__.defineProperty(this.__target__, key, descriptor_argument); | ||
| }; | ||
| // [RELEGATED] getOwnPropertyDescriptor must return an object or undefined. | ||
| // A property cannot be reported as non-existent, if it exists as a non-configurable own property of the target object. | ||
| // A property cannot be reported as non-existent, if it exists as an own property of the target object and the target object is not extensible. | ||
| // A property cannot be reported as existent, if it does not exists as an own property of the target object and the target object is not extensible. | ||
| // A property cannot be reported as non-configurable, if it does not exists as an own property of the target object or if it exists as a configurable own property of the target object. | ||
| // The result of Object.getOwnPropertyDescriptor(target) can be applied to the target object using Object.defineProperty and will not throw an exception. | ||
| handler.getOwnPropertyDescriptor = function (target, key) { | ||
| const descriptor = Reflect_getOwnPropertyDescriptor(target, key); | ||
| if (descriptor && !descriptor.configurable && !descriptor.writable) | ||
| return descriptor; | ||
| const __descriptor__ = this.__handler__.getOwnPropertyDescriptor(this.__target__, key); | ||
| if (__descriptor__ && !__descriptor__.configurable) | ||
| Reflect_defineProperty(target, key, __descriptor__); | ||
| if (!__descriptor__ && descriptor && !Reflect_isExtensible(target)) | ||
| Reflect_deleteProperty(target, key); | ||
| return __descriptor__; | ||
| }; | ||
| // A property cannot be deleted, if it exists as a non-configurable own property of the target object. | ||
| handler.deleteProperty = function (target, key) { | ||
| const descriptor = Reflect_getOwnPropertyDescriptor(target, key); | ||
| if (descriptor && !descriptor.configurable) | ||
| return false; | ||
| return this.__handler__.deleteProperty(this.__target__, key); | ||
| }; | ||
| // [RELEGATED] The result of ownKeys must be an array. | ||
| // [RELEGATED] The type of each array element is either a String or a Symbol. | ||
| // The result List must contain the keys of all non-configurable own properties of the target object. | ||
| // If the target object is not extensible, then the result List must contain all the keys of the own properties of the target object and no other values. | ||
| handler.ownKeys = function (target) { | ||
| if (Reflect_isExtensible(target)) | ||
| return this.__handler__.ownKeys(this.__target__); | ||
| const keys = Reflect_ownKeys(target); | ||
| const __keys__ = this.__handler__.ownKeys(this.__target__); | ||
| for (let index = 0, length = keys.length; index < length; index++) { | ||
| if (!__keys__.includes(keys[index])) { | ||
| Reflect_deleteProperty(target, keys[index]); | ||
| } | ||
| } | ||
| return __keys__; | ||
| }; | ||
| //////////////// | ||
| // Extensible // | ||
| //////////////// | ||
| // isExtensible >> Object.isExtensible(proxy) must return the same value as Object.isExtensible(target). | ||
| // preventExtensions >> Object.preventExtensions(proxy) only returns true if Object.isExtensible(proxy) is false. | ||
| // getPrototypeOf >> If target is not extensible, Object.getPrototypeOf(proxy) method must return the same value as Object.getPrototypeOf(target). | ||
| // ownKeys >> If the target object is not extensible, then the result List must contain all the keys of the own properties of the target object and no other values. | ||
| // getOwnPropertyDescriptor >> A property cannot be reported as existent, if it does not exists as an own property of the target object and the target object is not extensible. | ||
| handler.preventExtensionsHelper = function (target) { | ||
| const keys = this.__handler__.ownKeys(this.__target__); | ||
| for (let index = 0, length = keys.length; index < length; index++) { | ||
| if (!Reflect_getOwnPropertyDescriptor(target, keys[index])) { | ||
| Reflect_defineProperty(target, keys[index], {value:null, configurable:true}); | ||
| } | ||
| } | ||
| Reflect_setPrototypeOf(target, this.__handler__.getPrototypeOf(this.__target__)); | ||
| Reflect_preventExtensions(target); | ||
| }; | ||
| // Object.isExtensible(proxy) must return the same value as Object.isExtensible(target). | ||
| handler.isExtensible = function (target) { | ||
| if (Reflect_isExtensible(target)) { | ||
| if (this.__handler__.isExtensible(this.__target__)) | ||
| return true; | ||
| this.preventExtensionsHelper(target); | ||
| return false; | ||
| } | ||
| return false; | ||
| }; | ||
| // Object.preventExtensions(proxy) only returns true if Object.isExtensible(proxy) is false. | ||
| handler.preventExtensions = function (target) { | ||
| if (Reflect_isExtensible(target)) { | ||
| this.__handler__.preventExtensions(this.__target__); | ||
| this.preventExtensionsHelper(target); | ||
| } | ||
| return true; | ||
| }; | ||
| /////////////// | ||
| // Prototype // | ||
| /////////////// | ||
| // [RELEGATED] getPrototypeOf method must return an object or null. >> | ||
| // If target is not extensible, Object.getPrototypeOf(proxy) method must return the same value as Object.getPrototypeOf(target). | ||
| handler.getPrototypeOf = function (target) { | ||
| if (Reflect_isExtensible) | ||
| return this.__handler__.getPrototypeOf(this.__target__); | ||
| return Reflect_getPrototypeOf(target); | ||
| }; | ||
| // If target is not extensible, the prototype parameter must be the same value as Object.getPrototypeOf(target). | ||
| handler.setPrototypeOf = function (target, prototype) { | ||
| if (Reflect_isExtensible(target)) | ||
| return this.__handler__.setPrototypeOf(this.__target__, prototype); | ||
| return Reflect_getPrototypeOf(target) === prototype; | ||
| }; | ||
| ////////////////////// | ||
| // Property-Derived // | ||
| ////////////////////// | ||
| // A property cannot be reported as non-existent, if it exists as a non-configurable own property of the target object. | ||
| // A property cannot be reported as non-existent, if it exists as an own property of the target object and the target object is not extensible. | ||
| handler.has = function (target, key) { | ||
| const descriptor = Reflect_getOwnPropertyDescriptor(target, key); | ||
| if (descriptor && !descriptor.configurable) | ||
| return true; | ||
| if (this.__handler__.getOwnPropertyDescriptor(this.__target__, key)) | ||
| return true; | ||
| const prototype = Reflect_isExtensible(target) ? this.__handler__.getPrototypeOf(this.__target__) : Reflect_getPrototypeOf(target); | ||
| const result = Boolean(prototype) && Reflect_has(prototype, key); | ||
| if (!result && descriptor && !Reflect_isExtensible(target)) | ||
| Reflect_deleteProperty(target, key); | ||
| return result; | ||
| }; | ||
| // The value reported for a property must be the same as the value of the corresponding target object property if the target object property is a non-writable, non-configurable data property. | ||
| // The value reported for a property must be undefined if the corresponding target object property is non-configurable accessor property that has undefined as its [[Get]] attribute. | ||
| handler.get = function (target, key, receiver) { | ||
| const descriptor = Reflect_getOwnPropertyDescriptor(target, key); | ||
| if (descriptor && !descriptor.configurable) { | ||
| if (Reflect_getOwnPropertyDescriptor(descriptor, "value")) { | ||
| if (!descriptor.writable) { | ||
| return descriptor.value; | ||
| } | ||
| } else { | ||
| if (descriptor.get) { | ||
| return Reflect_apply(descriptor.get, receiver, []); | ||
| } | ||
| return void 0; | ||
| } | ||
| } | ||
| const __descriptor__ = this.__handler__.getOwnPropertyDescriptor(this.__target__, key); | ||
| if (__descriptor__) { | ||
| if (Reflect_getOwnPropertyDescriptor(__descriptor__, "value")) { | ||
| return __descriptor__.value; | ||
| } else { | ||
| if (__descriptor__.get) { | ||
| return Reflect_apply(__descriptor__.get, receiver, []); | ||
| } | ||
| return void 0; | ||
| } | ||
| } | ||
| const prototype = Reflect_isExtensible(target) ? this.__handler__.getPrototypeOf(this.__target__) : Reflect_getPrototypeOf(target); | ||
| if (prototype) | ||
| return Reflect_get(prototype, key, receiver); | ||
| return void 0; | ||
| }; | ||
| // https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver | ||
| // Cannot change the value of a property to be different from the value of the corresponding target object property if the corresponding target object property is a non-writable, non-configurable data property. | ||
| // Cannot set the value of a property if the corresponding target object property is a non-configurable accessor property that has undefined as its [[Set]] attribute. | ||
| // In strict mode, a false return value from the set handler will throw a TypeError exception. | ||
| handler.set = function (target, key, value, receiver) { | ||
| const descriptor = Reflect_getOwnPropertyDescriptor(target, key); | ||
| if (descriptor && !descriptor.configurable) { | ||
| if (Reflect_getOwnPropertyDescriptor(descriptor, "value")) { | ||
| if (!descriptor.writable) { | ||
| return false; | ||
| } | ||
| } else { | ||
| if (descriptor.set) { | ||
| Reflect_apply(descriptor.set, receiver, [value]); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| } | ||
| let __descriptor__ = this.__handler__.getOwnPropertyDescriptor(this.__target__, key); | ||
| if (!__descriptor__) { | ||
| const prototype = Reflect_isExtensible(target) ? this.__handler__.getPrototypeOf(this.__target__) : Reflect_getPrototypeOf(target); | ||
| if (prototype) | ||
| return Reflect_set(prototype, key, value, receiver); | ||
| __descriptor__ = {value:void 0, writable:true, enumerable:true, configurable:true}; | ||
| }; | ||
| if (Reflect_getOwnPropertyDescriptor(__descriptor__, "value")) { | ||
| if (!__descriptor__.writable) | ||
| return false; | ||
| if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) | ||
| return false; | ||
| const receiver_descriptor = Reflect_getOwnPropertyDescriptor(receiver, key) || {value:void 0, writable:true, enumerable:true, configurable:true}; | ||
| if (!Reflect_getOwnPropertyDescriptor(receiver_descriptor, "value")) | ||
| return false; | ||
| if (!receiver_descriptor.writable) | ||
| return false; | ||
| Reflect_setPrototypeOf(receiver_descriptor, null); | ||
| receiver_descriptor.value = value; | ||
| Reflect_defineProperty(receiver, key, receiver_descriptor); | ||
| return true; | ||
| } else { | ||
| if (__descriptor__.set) { | ||
| Reflect_apply(__descriptor__.set, receiver, [value]); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| } | ||
| module.exports = (target, __target__, __handler__) => { | ||
| if (__handler__ === void 0) { | ||
| __handler__ = __target__; | ||
| __target__ = target; | ||
| if (Array.isArray(__target__)) { | ||
| target = []; | ||
| } else if (typeof __target__ === "function") { | ||
| try { | ||
| Reflect_construct(Boolean, [], __target__); | ||
| target = function () { "use strict" }; | ||
| } catch (error) { | ||
| target = () => {}; | ||
| } | ||
| } else { | ||
| target = {}; | ||
| } | ||
| } | ||
| return new Proxy(target, {__proto__: handler, __target__, __handler__}); | ||
| }; | ||
| module.exports.Array = (__target__, __handler__) => module.exports([], __target__, __handler__); | ||
| module.exports.Object = (__target__, __handler__) => module.exports({}, __target__, __handler__); | ||
| module.exports.StrictFunction = (__target__, __handler__) => module.exports(function () { "use strict"; }, __target__, __handler__); | ||
| module.exports.Function = (__target__, __handler__) => module.exports(function () {}, __target__, __handler__); | ||
| module.exports.Arrow = (__target__, __handler__) => module.exports(() => {}, __target__, __handler__); | ||
| module.exports.StrictArrow = (__target__, __handler__) => module.exports(() => { "use strict"; }, __target__, __handler__); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
19302
3.88%5
25%443
54.36%Yes
NaN4
Infinity%50
-30.56%2
100%1
Infinity%