Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

proxserve

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

proxserve - npm Package Compare versions

Comparing version 1.1.0 to 1.2.0

222

index.js

@@ -27,4 +27,2 @@ "use strict"

let reservedFunctions = {}; //functions reserved as property names
let objectData = new WeakMap();
let proxyData = new WeakMap();

@@ -73,5 +71,6 @@ /**

* stop object and children from emitting change events
* @param {Object} target
*/
reservedFunctions.stop = function() {
objectData.get(this).status = statuses[1];
reservedFunctions.stop = function(target) {
this.objectData.get(target).status = statuses[1];
}

@@ -82,5 +81,6 @@

* user can't set nor delete any property
* @param {Object} target
*/
reservedFunctions.block = function() {
objectData.get(this).status = statuses[2];
reservedFunctions.block = function(target) {
this.objectData.get(target).status = statuses[2];
}

@@ -90,6 +90,7 @@

* resume default behavior of emitting change events, inherited from parent
* @param {Object} target
* @param {Boolean} [force] - force being active regardless of parent
*/
reservedFunctions.activate = function(force=false) {
let data = objectData.get(this);
reservedFunctions.activate = function(target, force=false) {
let data = this.objectData.get(target);
if(force || data.property==='') { //force activation or we are on root proxy

@@ -99,3 +100,3 @@ data.status = statuses[0];

else {
let data = objectData.get(this);
let data = this.objectData.get(target);
delete data.status;

@@ -107,2 +108,3 @@ }

* add event listener on a proxy
* @param {Object} target
* @param {String} event

@@ -112,5 +114,5 @@ * @param {Function} listener

*/
reservedFunctions.on = function(event, listener, id) {
reservedFunctions.on = function(target, event, listener, id) {
if(acceptableEvents.includes(event)) {
objectData.get(this).listeners.push([event, listener, id]);
this.objectData.get(target).listeners.push([event, listener, id]);
}

@@ -123,7 +125,8 @@ else {

/**
*
* @param {String} id - removing listener(s) from an object by an identifier
* removes a listener from an object by an identifier (can have multiple listeners with the same ID)
* @param {Object} target
* @param {String} id - the listener(s) identifier
*/
reservedFunctions.removeListener = function(id) {
let listeners = objectData.get(this).listeners;
reservedFunctions.removeListener = function(target, id) {
let listeners = this.objectData.get(target).listeners;
for(let i = listeners.length - 1; i >= 0; i--) {

@@ -138,8 +141,28 @@ if(listeners[i][2] === id) {

* removing all listeners of an object
* @param {Object} target
*/
reservedFunctions.removeAllListeners = function() {
objectData.get(this).listeners = [];
reservedFunctions.removeAllListeners = function(target) {
this.objectData.get(target).listeners = [];
}
/**
* the following functions (getOriginalTarget, getProxserveInstance) seem silly because they could have been written directly
* on the handler's get() method but it's here as part of the convention of exposing proxy-"inherited"-methods
*/
/**
* get original target that is behind the proxy.
* @param {Object} target
*/
reservedFunctions.getOriginalTarget = function(target) {
return target;
}
/**
* get the Proxserve's instance that created this proxy
*/
reservedFunctions.getProxserveInstance = function() {
return this;
}
/**
* save an array of all reserved function names

@@ -187,3 +210,3 @@ * and also add synonyms to these functions

let data = objectData.get(target);
let data = this.objectData.get(target);

@@ -200,4 +223,8 @@ if(data.status === statuses[1]) { //stopped

if(data.delay <= 0) emit(target); //emit immediately
else if(data.eventPool.length === 1) setTimeout(emit, data.delay, target); //initiate timeout once, when starting to accumulate events
if(this.delay <= 0) {
emit.call(this, target); //emit immediately
}
else if(data.eventPool.length === 1) {
setTimeout(emit.bind(this), this.delay, target); //initiate timeout once, when starting to accumulate events
}
}

@@ -208,3 +235,3 @@

if(data !== Object.prototype) {
add2emitQueue(data.target, path, oldValue, value, changeType);
add2emitQueue.call(this, data.target, path, oldValue, value, changeType);
}

@@ -214,6 +241,5 @@ }

function emit(target) {
let data = objectData.get(target);
let onChangeListeners = [];
let data = this.objectData.get(target);
for(let change of data.eventPool) { //first event in first event out
for(let change of data.eventPool) { //FIFO - first event in, first event out
for(let listener of data.listeners) { //listener = [event, function]

@@ -223,10 +249,9 @@ if(listener[0] === change.type) { //will invoke create/update/delete listeners one by one.

}
else if(listener[0] === acceptableEvents[0]) { //change
onChangeListeners.push(listener);
}
}
}
for(let listener of onChangeListeners) {
listener[1].call(data.proxy, data.eventPool); //on(change) always gets an array of one or more changes
for(let listener of data.listeners) {
if(listener[0] === acceptableEvents[0]) { //change
listener[1].call(data.proxy, data.eventPool); //on(change) is always called with an array of one or more changes
}
}

@@ -239,3 +264,3 @@

/**
* construct a new proxy from a target object
* construct a new proxserve instance
* @param {Object|Array} target

@@ -246,19 +271,23 @@ * @param {Object} [options]

* @property {Boolean} [options.emitReference] - events emit new/old values. true: reference to original objects, false: deep clones that are created on the spot
* @param {Object|Array} arguments[2] - parent
* @param {String} arguments[3] - path
* @param {String} arguments[4] - current property
*/
constructor(target, options={}) {
if(typeof options.delay === 'undefined') options.delay = 10;
if(typeof options.strict === 'undefined') options.strict = true;
if(typeof options.emitReference === 'undefined') options.emitReference = true;
constructor(target, options = {}) {
this.delay = (options.delay !== undefined) ? options.delay : 10;
this.strict = (options.strict !== undefined) ? options.strict : true;
this.emitReference = (options.emitReference !== undefined) ? options.emitReference : true;
this.objectData = new WeakMap();
this.proxyData = new WeakMap();
let parent = null, path = '', currentProperty = '', currentPathProperty = '';
if(arguments.length > 2) {
parent = arguments[2]; //the parent target
path = arguments[3]; //the path up to this target
currentProperty = arguments[4];
currentPathProperty = property2path(target, currentProperty);
}
return this.createProxy(target, null, '', '');
}
/**
* create a new proxy object from a target object
* @param {Object|Array} target
* @param {Object|Array} parent
* @param {String} path
* @param {String} currentProperty
*/
createProxy(target, parent, path, currentProperty) {
let currentPathProperty = (currentProperty === '') ? '' : property2path(target, currentProperty);
let typeoftarget = realtypeof(target);

@@ -268,14 +297,15 @@

let revocable = Proxy.revocable(target, {
get: function(target, property, receiver) {
get: (target, property, receiver) => {
//can access a function (or its synonym) if their keywords isn't used
if(reservedFunctionsNames.includes(property) && typeof target[property] === 'undefined') {
return reservedFunctions[property].bind(target);
return reservedFunctions[property].bind(this, target);
}
else {
return target[property];
let data = this.objectData.get(target[property]);
return (data !== undefined) ? data.proxy : target[property];
}
},
set: function(target, property, value, receiver) {
let data = objectData.get(target);
set: (target, property, value, receiver) => {
let data = this.objectData.get(target);
if(data.status === statuses[2]) { //blocked from changing values

@@ -293,24 +323,25 @@ console.error(`can't change value of property '${property}'. object is blocked.`);

if(acceptableTypes.includes(typeofvalue)) {
value = new Proxserve(value, options, target, `${path}${currentPathProperty}`, property); //if trying to add a new value which is an object then make it a proxy
this.createProxy(value, target, `${path}${currentPathProperty}`, property); //if trying to add a new value which is an object then make it a proxy
}
let oldValue = target[property];
if(!options.emitReference) {
oldValue = simpleClone(target[property]); //also prevents emitting proxies
}
target[property] = value; //assign new value
let oldValue = data.proxy[property];
if(options.strict && proxyData.has(oldValue)) { //a proxy has been detached from the tree
if(this.strict && this.proxyData.has(oldValue)) { //a proxy has been detached from the tree
Proxserve.destroy(oldValue);
}
let newValue = value;
if(!options.emitReference) {
newValue = simpleClone(value); //also prevents emitting proxies
if(!this.emitReference) { //also prevents emitting proxies
newValue = simpleClone(value);
oldValue = simpleClone(target[property]);
}
add2emitQueue(target, property2path(target, property), oldValue, newValue);
target[property] = value; //assign new value
add2emitQueue.call(this, target, property2path(target, property), oldValue, newValue);
return true;
},
deleteProperty: function(target, property) {
if(objectData.get(target).status === statuses[2]) { //blocked from changing values
deleteProperty: (target, property) => {
let data = this.objectData.get(target);
if(data.status === statuses[2]) { //blocked from changing values
console.error(`can't delete property '${property}'. object is blocked.`);

@@ -321,11 +352,8 @@ return true;

if(property in target) {
let oldValue = target[property];
if(!options.emitReference) {
oldValue = simpleClone(target[property]); //also prevents emitting proxies
let oldValue = data.proxy[property];
if(this.strict) {
Proxserve.destroy(oldValue);
}
if(options.strict) {
Proxserve.destroy(target[property]);
}
let propertyData = proxyData.get(target[property]);
let propertyData = this.objectData.get(target[property]);
if(propertyData) {

@@ -335,5 +363,8 @@ propertyData.status = statuses[3]; //deleted

if(!this.emitReference) {
oldValue = simpleClone(target[property]); //also prevents emitting proxies
}
delete target[property]; //actual delete
add2emitQueue(target, property2path(target, property), oldValue, undefined, 'delete');
add2emitQueue.call(this, target, property2path(target, property), oldValue, undefined, 'delete');
return true;

@@ -351,3 +382,2 @@ }

'status': statuses[0],
'delay': options.delay,
'pathProperty': ''

@@ -358,3 +388,3 @@ };

//inherit from parent
data = Object.create(objectData.get(parent));
data = Object.create(this.objectData.get(parent));
data.pathProperty = property2path(parent, currentProperty);

@@ -375,4 +405,4 @@ }

//save important data regarding the proxy and original (raw) object
objectData.set(target, data);
proxyData.set(revocable.proxy, data);
this.objectData.set(target, data);
this.proxyData.set(revocable.proxy, data);

@@ -384,3 +414,3 @@ if(typeoftarget === 'Object') {

if(acceptableTypes.includes(typeofproperty)) {
target[key] = new Proxserve(target[key], options, target, `${path}${currentPathProperty}`, key); //recursively make child objects also proxies
this.createProxy(target[key], target, `${path}${currentPathProperty}`, key); //recursively make child objects also proxies
}

@@ -393,3 +423,3 @@ }

if(acceptableTypes.includes(typeofproperty)) {
target[i] = new Proxserve(target[i], options, target, `${path}${currentPathProperty}`, i); //recursively make child objects also proxies
this.createProxy(target[i], target, `${path}${currentPathProperty}`, i); //recursively make child objects also proxies
}

@@ -415,22 +445,27 @@ }

static destroy(proxy) {
let data = proxyData.get(proxy);
let self;
try {
self = proxy.getProxserveInstance();
} catch(error) {
return; //proxy variable isn't a proxy
}
let data = self.proxyData.get(proxy);
if(data) {
let typeoftarget = realtypeof(data.target);
if(acceptableTypes.includes(typeoftarget)) {
let target = data.target;
if(typeoftarget === 'Object') {
let keys = Object.keys(target);
let typeofproxy = realtypeof(proxy);
if(acceptableTypes.includes(typeofproxy)) {
if(typeofproxy === 'Object') {
let keys = Object.keys(proxy);
for(let key of keys) {
let typeofproperty = realtypeof(target[key]);
let typeofproperty = realtypeof(proxy[key]);
if(acceptableTypes.includes(typeofproperty)) {
Proxserve.destroy(target[key]);
Proxserve.destroy(proxy[key]);
}
}
}
else if(typeoftarget === 'Array') {
for(let i = target.length - 1; i >= 0; i--) {
let typeofproperty = realtypeof(target[i]);
else if(typeofproxy === 'Array') {
for(let i = proxy.length - 1; i >= 0; i--) {
let typeofproperty = realtypeof(proxy[i]);
if(acceptableTypes.includes(typeofproperty)) {
Proxserve.destroy(target[i]);
Proxserve.destroy(proxy[i]);
}

@@ -445,3 +480,3 @@ }

data.revoke();
}, data.delay + 1000);
}, self.delay + 1000);
}

@@ -453,2 +488,3 @@ }

* splits a path to an array of properties
* (benchmarked and is faster than regex and split())
* @param {String} path

@@ -478,3 +514,3 @@ */

/**
*
* get the target matching the path from object
* @param {Proxy|Object} obj

@@ -486,3 +522,3 @@ * @param {String} path

let i;
for(i = 0; i <= segments.length - 2; i++) {
for(i = 0; i <= segments.length - 2; i++) { //iterate until one before last property because they all must exist
obj = obj[segments[i]];

@@ -493,3 +529,3 @@ if(typeof obj === 'undefined') {

}
return obj[segments[i]];
return obj[segments[i]]; //return last property. it can be undefined
}

@@ -496,0 +532,0 @@ }

{
"name": "proxserve",
"version": "1.1.0",
"version": "1.2.0",
"description": "Proxy Observe on objects and properties changes",

@@ -5,0 +5,0 @@ "license": "Apache 2.0",

@@ -17,2 +17,12 @@ "use strict"

var consoleFuncs = { log: console.log, warn: console.warn, error: console.error };
function silentConsole() {
console.log = console.warn = console.error = function() { };
}
function wakeConsole() {
console.log = consoleFuncs.log;
console.warn = consoleFuncs.warn;
console.error = consoleFuncs.error;
}
function deepCountObjects(obj) {

@@ -70,8 +80,10 @@ let numChildObjects = 0;

test('Initiate a proxserve and check if original object stays intact', () => {
let proxy = new Proxserve(cloneDeep(testObject));
test('1. Initiate a proxserve and check if original object stays intact', () => {
let origin = cloneDeep(testObject);
let proxy = new Proxserve(origin);
expect(proxy).toEqual(testObject);
expect(proxy.getOriginalTarget() === origin).toBe(true);
});
test('Object, child-objects and added-child-objects should convert to proxies', () => {
test('2. Object, child-objects and added-child-objects should convert to proxies', () => {
let proxy = new Proxserve(cloneDeep(testObject));

@@ -89,3 +101,3 @@ proxy.level1_3 = {

test('Proxies should contain built-in functions', () => {
test('3. Proxies should contain built-in functions', () => {
let proxy = new Proxserve(cloneDeep(testObject));

@@ -105,2 +117,6 @@

expect(typeof proxy.$activate).toBe('function');
expect(typeof proxy.getOriginalTarget).toBe('function');
expect(typeof proxy.$getOriginalTarget).toBe('function');
expect(typeof proxy.getProxserveInstance).toBe('function');
expect(typeof proxy.$getProxserveInstance).toBe('function');

@@ -121,3 +137,3 @@ expect(typeof proxy.level1_1.arr1.on).toBe('function');

test('Basic events of changes', (done) => {
test('4. Basic events of changes', (done) => {
let proxy = new Proxserve(cloneDeep(testObject));

@@ -175,4 +191,4 @@ proxy.on('create', function(change) {

test('Delay of events', (done) => {
let proxy = new Proxserve(cloneDeep(testObject), {delay:0});
test('5. Delay of events', (done) => {
let proxy = new Proxserve(cloneDeep(testObject), { delay: 0 });
let changes = [];

@@ -187,6 +203,6 @@ proxy.on('change', function(change) {

proxy = new Proxserve(cloneDeep(testObject), {delay:10});
proxy = new Proxserve(cloneDeep(testObject), { delay: 10 });
changes.length = 0;
proxy.on('change', function(change) {
changes.push(1);
proxy.on('change', function(batchOfChanges) {
changes.push(...batchOfChanges);
});

@@ -203,6 +219,6 @@ proxy.num = 5;

function part2() {
proxy = new Proxserve(cloneDeep(testObject), {delay:500});
proxy = new Proxserve(cloneDeep(testObject), { delay: 500 });
changes.length = 0;
proxy.on('change', function(change) {
changes.push(1);
proxy.on('change', function(batchOfChanges) {
changes.push(...batchOfChanges);
});

@@ -225,3 +241,84 @@ proxy.num = 5;

test('Destroy proxy and sub-proxies', (done) => {
test('6. Stop/Block/Activate proxies', () => {
let proxy = new Proxserve(cloneDeep(testObject), {delay:0});
let numberOfEmits = 0;
proxy.on('change', function(changes) {
numberOfEmits++;
});
proxy.level1_1.arr1[1] = 12;
expect(numberOfEmits).toBe(1);
proxy.stop();
proxy.level1_1.arr1[1] = 13;
expect(numberOfEmits).toBe(1);
proxy.activate();
proxy.level1_1.arr1[1] = 14;
expect(numberOfEmits).toBe(2);
proxy.block();
silentConsole();
proxy.level1_1.arr1[1] = 555;
wakeConsole();
expect(proxy.level1_1.arr1[1]).toBe(14);
expect(numberOfEmits).toBe(2);
proxy.activate();
proxy.level1_1.arr1[1] = 15;
expect(proxy.level1_1.arr1[1]).toBe(15);
expect(numberOfEmits).toBe(3);
numberOfEmits = 0;
proxy.removeAllListeners();
proxy.level1_2.on('change', function(changes) {
numberOfEmits++;
});
proxy.level1_2.level2_1.level3_1.on('change', function(changes) {
numberOfEmits++;
});
proxy.level1_2.level2_1.level3_1.arr2[0] = 12;
expect(numberOfEmits).toBe(2); //two listeners were called
//test stop
proxy.level1_2.level2_1.stop();
proxy.level1_2.level2_1.level3_1.activate();
proxy.level1_2.level2_1.level3_1.arr2[0] = 13;
expect(numberOfEmits).toBe(2); //both objects inherit the 'stopped' status
proxy.level1_2.level2_1.level3_1.activate(true);
proxy.level1_2.level2_1.level3_1.arr2[0] = 14;
expect(numberOfEmits).toBe(3); //only one object has the 'stopped' status
proxy.level1_2.level2_1.level3_1.activate(); //inherits from parent again
proxy.level1_2.level2_1.level3_1.arr2[0] = 13;
expect(numberOfEmits).toBe(3);
//test block
silentConsole();
proxy.level1_2.level2_1.block();
proxy.level1_2.level2_1.level3_1.activate();
proxy.level1_2.level2_1.level3_1.arr2[0] = 555;
expect(proxy.level1_2.level2_1.level3_1.arr2[0]).toBe(13); //both objects inherit the 'blocked' status
expect(numberOfEmits).toBe(3);
proxy.level1_2.level2_1.level3_1.activate(true);
proxy.level1_2.level2_1.level3_1.arr2[0] = 555;
expect(proxy.level1_2.level2_1.level3_1.arr2[0]).toBe(555); //only one object has the 'blocked' status
expect(numberOfEmits).toBe(5); //even though parent is 'blocked', the child did mutate and event was emitted to all parents
proxy.level1_2.level2_1.level3_1.activate(); //inherits from parent again
proxy.level1_2.level2_1.level3_1.arr2[0] = 14;
expect(proxy.level1_2.level2_1.level3_1.arr2[0]).toBe(555);
expect(numberOfEmits).toBe(5);
proxy.block();
proxy.level1_2.stop(); //stopped is not blocked
proxy.level1_2.level2_1.level3_1.activate(true);
proxy.level1_2.level2_1.level3_1.arr2[0] = 15;
expect(proxy.level1_2.level2_1.level3_1.arr2[0]).toBe(15);
expect(numberOfEmits).toBe(6);
wakeConsole();
});
test('7. Destroy proxy and sub-proxies', (done) => {
let proxy = new Proxserve(cloneDeep(testObject), {delay:-950}); //hack to decrease the 1000ms delay of destroy

@@ -251,3 +348,3 @@ Proxserve.destroy(proxy);

function part3() {
proxy = new Proxserve(cloneDeep(testObject), {delay:-950});
proxy = new Proxserve(cloneDeep(testObject), {delay:-950, strict: true});
let reference2level3 = proxy.level1_2.level2_1.level3_1;

@@ -270,3 +367,3 @@ let reference2arr2 = reference2level3.arr2;

test('Keep using proxies after deletion/detachment in non-strict instantiation', (done) => {
test('8. Keep using proxies after deletion/detachment in non-strict instantiation', (done) => {
let proxy = new Proxserve(cloneDeep(testObject), {delay:-1000, strict:false});

@@ -286,3 +383,3 @@ let level1_1 = proxy.level1_1;

test('Observe on referenced changes and cloned changes', (done) => {
test('9. Observe on referenced changes and cloned changes', (done) => {
let proxy = new Proxserve(cloneDeep(testObject), {emitReference: false});

@@ -320,3 +417,3 @@ proxy.level1_1.on('change', function(changes) {

//benchmark on a CPU with baseclock of 3.6 GHz is around 0.5s
test('Proxserve 50,000 objects in less than 1 second', () => {
test('10. Proxserve 50,000 objects in less than 1 second', () => {
let objectsInTest = deepCountObjects(testObject);

@@ -341,3 +438,3 @@ let repeatitions = Math.ceil(50000 / objectsInTest);

//benchmark on a CPU with baseclock of 3.6 GHz is around 0.9s
test('Destroy 50,000 proxserves in less than 1.5 seconds', (done) => {
test('11. Destroy 50,000 proxserves in less than 1.5 seconds', (done) => {
let objectsInTest = deepCountObjects(testObject);

@@ -357,2 +454,3 @@ let repeatitions = Math.ceil(50000 / objectsInTest);

let end = Date.now();
proxies;
expect(end - start - 20).toBeLessThan(1500);

@@ -363,3 +461,3 @@ done();

test('Comprehensive events of changes', (done) => {
test('12. Comprehensive events of changes', (done) => {
let proxy = new Proxserve(cloneDeep(testObject), {emitReference: false});

@@ -522,3 +620,3 @@ proxy.on('create', function(change) {

test('splitPath - split path to segments', () => {
test('13. splitPath - split path to segments', () => {
let path = Proxserve.splitPath('.level2_1.level3_1');

@@ -546,3 +644,3 @@ expect(path).toEqual(['level2_1','level3_1']);

test('getPathTarget - get target property of object and path', (done) => {
test('14. getPathTarget - get target property of object and path', (done) => {
let proxy = new Proxserve(cloneDeep(testObject), {delay: 0});

@@ -549,0 +647,0 @@ proxy.on('change', function(changes) {

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc