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

on-change

Package Overview
Dependencies
Maintainers
1
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

on-change - npm Package Compare versions

Comparing version 1.1.0 to 1.2.0

96

index.d.ts

@@ -1,11 +0,85 @@

/**
* Watch an object or array for changes. It works recursively, so it will even detect if you modify a deep property like `obj.a.b[0].c = true`.
*
* @param object - Object to watch for changes.
* @param onChange - Function that gets called anytime the object changes.
* @returns A version of `object` that is watched. It's the exact same object, just with some `Proxy` traps.
*/
export default function onChange<ObjectType extends {[key: string]: unknown}>(
object: ObjectType,
onChange: () => void
): ObjectType;
declare const onChange: {
/**
Watch an object or array for changes. It works recursively, so it will even detect if you modify a deep property like `obj.a.b[0].c = true`.
@param object - Object to watch for changes.
@param onChange - Function that gets called anytime the object changes.
@returns A version of `object` that is watched. It's the exact same object, just with some `Proxy` traps.
@example
```
import onChange = require('on-change');
const object = {
foo: false,
a: {
b: [
{
c: false
}
]
}
};
let i = 0;
const watchedObject = onChange(object, function (path, value, previousValue) {
console.log('Object changed:', ++i);
console.log('this:', this);
console.log('path:', path);
console.log('value:', value);
console.log('previousValue:', previousValue);
});
watchedObject.foo = true;
//=> 'Object changed: 1'
//=> 'this: {
// foo: true,
// a: {
// b: [
// {
// c: false
// }
// ]
// }
// }'
//=> 'path: "foo"'
//=> 'value: true'
//=> 'previousValue: false'
watchedObject.a.b[0].c = true;
//=> 'Object changed: 2'
//=> 'this: {
// foo: true,
// a: {
// b: [
// {
// c: true
// }
// ]
// }
// }'
//=> 'path: "a.b.0.c"'
//=> 'value: true'
//=> 'previousValue: false'
```
*/
<ObjectType extends {[key: string]: unknown}>(
object: ObjectType,
onChange: (
this: ObjectType,
path: string,
value: unknown,
previousValue: unknown
) => void
): ObjectType;
// TODO: Remove this for the next major release, refactor the whole definition to:
// declare function onChange<ObjectType extends {[key: string]: unknown}>(
// object: ObjectType,
// onChange: (this: ObjectType, path: string, value: unknown, previousValue: unknown) => void
// ): ObjectType;
// export = onChange;
default: typeof onChange;
};
export = onChange;

61

index.js

@@ -5,2 +5,14 @@ 'use strict';

const concatPath = (path, property) => {
if (property && property.toString) {
if (path) {
path += '.';
}
path += property.toString();
}
return path;
};
const proxyTarget = Symbol('ProxyTarget');

@@ -12,6 +24,7 @@

const propCache = new WeakMap();
const pathCache = new WeakMap();
const handleChange = () => {
const handleChange = (path, property, previous, value) => {
if (!inApply) {
onChange();
onChange.call(proxy, concatPath(path, property), value, previous);
} else if (!changed) {

@@ -23,14 +36,17 @@ changed = true;

const getOwnPropertyDescriptor = (target, property) => {
if (!propCache.has(target)) {
propCache.set(target, new Map());
let props = propCache.get(target);
if (props) {
return props;
}
const props = propCache.get(target);
if (props.has(property)) {
return props.get(property);
props = new Map();
propCache.set(target, props);
let prop = props.get(property);
if (!prop) {
prop = Reflect.getOwnPropertyDescriptor(target, property);
props.set(property, prop);
}
const prop = Reflect.getOwnPropertyDescriptor(target, property);
props.set(property, prop);
return prop;

@@ -40,8 +56,7 @@ };

const invalidateCachedDescriptor = (target, property) => {
if (!propCache.has(target)) {
return;
const props = propCache.get(target);
if (props) {
props.delete(property);
}
const props = propCache.get(target);
props.delete(property);
};

@@ -72,2 +87,3 @@

pathCache.set(value, concatPath(pathCache.get(target), property));
return new Proxy(value, handler);

@@ -81,7 +97,7 @@ },

const previous = Reflect.get(target, property, value, receiver);
const previous = Reflect.get(target, property, receiver);
const result = Reflect.set(target, property, value);
if (previous !== value) {
handleChange();
handleChange(pathCache.get(target), property, previous, value);
}

@@ -96,3 +112,3 @@

handleChange();
handleChange(pathCache.get(target), property, undefined, descriptor.value);

@@ -103,6 +119,7 @@ return result;

deleteProperty(target, property) {
const previous = Reflect.get(target, property);
const result = Reflect.deleteProperty(target, property);
invalidateCachedDescriptor(target, property);
handleChange();
handleChange(pathCache.get(target), property, previous);

@@ -132,6 +149,10 @@ return result;

return new Proxy(object, handler);
pathCache.set(object, '');
const proxy = new Proxy(object, handler);
return proxy;
};
module.exports = onChange;
// TODO: Remove this for the next major release
module.exports.default = onChange;
{
"name": "on-change",
"version": "1.1.0",
"version": "1.2.0",
"description": "Watch an object or array for changes",

@@ -16,3 +16,3 @@ "license": "MIT",

"scripts": {
"test": "xo && ava && tsd-check",
"test": "xo && ava && tsd",
"bench": "matcha bench/bench.js"

@@ -41,7 +41,7 @@ },

"devDependencies": {
"ava": "^1.0.1",
"ava": "^1.4.1",
"matcha": "^0.7.0",
"tsd-check": "^0.3.0",
"tsd": "^0.7.1",
"xo": "^0.24.0"
}
}

@@ -34,4 +34,8 @@ # on-change [![Build Status](https://travis-ci.org/sindresorhus/on-change.svg?branch=master)](https://travis-ci.org/sindresorhus/on-change)

let i = 0;
const watchedObject = onChange(object, () => {
const watchedObject = onChange(object, function (path, value, previousValue) {
console.log('Object changed:', ++i);
console.log('this:', this);
console.log('path:', path);
console.log('value:', value);
console.log('previousValue:', previousValue);
});

@@ -41,5 +45,31 @@

//=> 'Object changed: 1'
//=> 'this: {
// foo: true,
// a: {
// b: [
// {
// c: false
// }
// ]
// }
// }'
//=> 'path: "foo"'
//=> 'value: true'
//=> 'previousValue: false'
watchedObject.a.b[0].c = true;
//=> 'Object changed: 2'
//=> 'this: {
// foo: true,
// a: {
// b: [
// {
// c: true
// }
// ]
// }
// }'
//=> 'path: "a.b.0.c"'
//=> 'value: true'
//=> 'previousValue: false'
```

@@ -66,3 +96,10 @@

The function receives three arguments:
1. A path to the value that was changed. A change to `c` in the above example would return `a.b.0.c`.
2. The new value at the path.
3. The previous value at the path.
The context (this) is set to the original object passed to `onChange` (with Proxy).
## Use-case

@@ -69,0 +106,0 @@

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