npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reversecurrent/track-changes

v0.2.0

Published

Watch an object or array for changes

Readme

track-changes

Track an object or array for changes

Target object can have nested objects/arrays which will also be tracked Uses the Proxy API and Reflect API.

Install

$ npm install track-changes

Usage

// Import the 
import trackChanges from 'track-changes';
// Define the target object to track for changes.
// Target object can have nested objects/arrays which will also be tracked
let target = {
    name: 'furqan',
    age: 30,
    isSmart: false,
    address: {
        geo: {
            lat: 23.56,
            lon: 56.78,
            encoding: {
                type: 'map'
            }
        },
        city: 'bangalore',
        zipcode: '56004'
    },
    companies: {
        professional: [
            {
                name: 'Amazon',
                title: 'Software Engineer'
            },
            {
                name: 'Microsoft',
                title: 'Technical Lead'
            }
        ],
        freelancing: [
            {
                name: 'Reverse Current',
                title: 'Consultant'
            }
        ]
    },
    hobbies: ['tennis']
};

// Define the tracker function which will be invoked when the target object
// is accessed/modified
const trackerFn = (opType, propertyKey, newValue, oldValue) => {
                console.log(opType); // Set | Get | Delete | Sort | Assign
                console.log(propertyKey); // Property name being accessed/modified/deleted
                console.log(newValue); // New value of the property. null for Delete
                console.log(oldValue); // Old value of the property. null for Get , Delete
            };
// Call the trackChanges function passing the target object and tracker function
// It will return a proxy object using which  we access/modify the target
let watchedObject = trackChanges(target, trackerFn);

// Set a property value. This will:
//   1. Set the value in target property
//   2. Invoke tracker function passing the parameters
watchedObject.name = 'Furqan - Modified';
// opType = 'Set' , propertyKey = 'name', newValue = 'Furqan -modified', oldValue = 'Furqan'

// Set a nested property value
watchedObject.address.geo.encoding.type = 'geo';
// opType = 'Set' , propertyKey = 'type', newValue = 'geo', oldValue = 'map'

API

trackChanges(target, trackerFn)

Returns a version of target that is tracked for changes. It's the exact same object, with some Proxy traps.

target

Type: Object

Object to watch for changes.

trackerFn

Type: Function Function that is invoked when target changes/accessed

opType

Type: String Operation being performed on the target. One of these values: 'Get', 'Set', 'Delete', 'Assign', 'Sort'

propertyKey

Type: String Property name being changed/accessed

newValue

Type: any New value of the property

oldValue

Type: any Old value of the property

Scenarios

Track when target properties are accessed

const trackerFn = (opType, propertyKey, newValue, oldValue) => {
                console.log(opType);  // OpType.Get
                console.log(propertyKey) // 'name' | 'type'
                console.log(newValue) // 'furqan' | 'map'
            };
            const proxy = trackChanges(target, trackerFn);
            // Accessing a property will invoke the tracker function
            proxy.name;
            // Accessing a nested property will also invoke the tracker function
            proxy.address.geo.encoding.type;

Track when target properties (including nested) are modified

const trackerFn = (opType, propertyKey, newValue, oldValue) => {
                console.log(opType);  // OpType.Set
                console.log(propertyKey) // 'name'('type' when type nested property is set)
                console.log(newValue) // 'Furqan - modified' ('geo' when type nested property is set)
            };
            const proxy = trackChanges(target, trackerFn);
            // Setting a property will invoke the tracker function
            proxy.name = 'Furqan - modified';
            // Setting a nested property will also invoke the tracker function
            proxy.address.geo.encoding.type = 'geo';

Track when nested array properties are modified

const trackerFn = (opType, propertyKey, newValue, oldValue) => {
                console.log(opType);  // OpType.Set
                console.log(propertyKey) // 'professional'
                console.log(newValue) // [
                                            // {name: 'Amazon',title: 'Software Engineer'},
                                            // {name: 'Microsoft',title: 'Technical Lead'},
                console.log(oldValue) // [
                                            // {name: 'Amazon',title: 'Software Engineer'},
                                            // {name: 'Microsoft',title: 'Technical Lead'},
                                            //{ 'name': 'Google', 'title': 'Design Lead' } ]
            };
const proxy = trackChanges(target, trackerFn);
// Setting a nested array property will invoke the tracker function
proxy.companies.professional.push({ 'name': 'Google', 'title': 'Design Lead' });

Track when array value is get/set using index

let hobbies = ['tennis'];
            const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Get);
                expect(propertyKey).toEqual('0'); 
                expect(value).toEqual('tennis');
            };
            const proxy = trackChanges(hobbies, trackerFn);
            // Accessing an array item using index will invoke the tracker function
            proxy[0];


            // Setting an array item will invoke the tracker function
            const trackerFn2 = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Set);
                expect(propertyKey).toEqual('1'); 
                expect(value).toEqual('reading');
            };
            const proxy2 = trackChanges(hobbies, trackerFn2);
            proxy2[1] = 'reading';

Track when setting array value using push

let hobbies = ['tennis'];
            const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Get);
                expect(propertyKey).toEqual('push');
                expect(value).toEqual(['tennis', 'reading']);
            };
            const proxy = trackChanges(hobbies, trackerFn);
            // Setting an array item using push will invoke the tracker function
            proxy.push('reading');

Track deleting property on target

            const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Delete);
                expect(propertyKey).toEqual('type');
            };
            const proxy = trackChanges(target, trackerFn);
            delete proxy.address.geo.encoding.type;

Track when calling Object.assign on targets

            const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Set);
                expect(propertyKey).toEqual('phone');
                expect(newValue).toEqual('1234');
            };
            const proxy = trackChanges(target, trackerFn);
            Object.assign(proxy, { 'phone': '1234' })

Track when setting a nested object on an array target

let array = [1, 2, { a: 3 }];
const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Set);
                expect(newValue).toEqual(4);
};
const proxy = trackChanges(array, trackerFn);
proxy[2].a = 4;

Track when calling sort on array target

let array = [4,2,1,6,7];
const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Sort);
                expect(newValue).toEqual([1,2,4,6,7]);
};
const proxy = trackChanges(array, trackerFn);
proxy.sort();

Allow only access to known object properties

const obj = {foo: true};
const proxy = trackChanges(obj, trackerFn);
// Throws a TypeError when you try to access an unknown property
console.log(proxy.bar);
//=> [TypeError] Unknown property: bar