New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

virtual-proxy

Package Overview
Dependencies
Maintainers
0
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

virtual-proxy - npm Package Compare versions

Comparing version
0.0.11
to
0.1.0
+381
lib/index.mjs
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,
);
};
/**
* 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)
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__);