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 🙏

© 2024 – Pkg Stats / Ryan Hefner

object-monitor

v0.1.1

Published

monitoring change of object properties

Downloads

53

Readme

object-monitor Build Status

A NodeJS module for monitoring change of object properties.

License

MIT

Installation

npm install --save object-monitor

Usage

const Monitor = require( "object-monitor" );

let data = {
    someObject: {
        subProperty: "its value",
    },
};

// create monitor on some data object
const monitor = Monitor( data, { recursive: true } );

// inspect monitoring context
console.log( monitor.$context.hasChanged ); // -> false
console.log( monitor.someObject.subProperty ); // -> "its value"

// add another property
monitor.someString = "another info";

// inspect monitoring context again
console.log( monitor.$context.hasChanged ); // -> true
console.log( monitor.$context.changed.has( "someString" ) ); // -> true
for ( let key of monitor.$context.changed.keys() ) {
    console.log( key ); // -> someString
}

// replace some existing nested property
monitor.someObject.subProperty = "new value";

// original value is tracked in monitoring context
console.log( monitor.$context.changed.get( "someObject.subProperty" ) ); // -> "its value"

// revert all changes (or try committing instead)
monitor.$context.rollBack();

// inspect monitoring context again
console.log( monitor.$context.hasChanged ); // -> false

// changed property has been reverted
console.log( monitor.someObject.subProperty ); // -> "its value"

Type coercion

In v0.0.7 support for implicit type/value coercion handlers has been added.

const Monitor = require( "object-monitor" );

let data = {
    someObject: {
        subProperty: "its value",
        subObject: {},
    },
};

// create monitor on some data object
const monitor = Monitor( data, {
	recursive: true,
	coercion: {
		someValue: ( value, label ) => `new value of ${label} is "${value}"`,
		"someObject.subProperty": ( value, label ) => `deep change of ${label} to "${value}"`,
		"*.deepSub": ( value, label ) => `value of ${label} is now "${value}"`,
		"*": ( value, label ) => `fallback value of ${label} is now "${value}"`
	},
} );

monitor.someValue = "added";
monitor.someObject.subProperty = "new value";
monitor.someObject.subObject.deepSub = "added";
monitor.someObject.subObject.anotherSub = 1000;


console.log( monitor.someValue );
// new value of someValue is "added"

console.log( monitor.someObject.subProperty );
// deep change of someObject.subProperty to "new value"

console.log( monitor.someObject.subObject.deepSub );
// value of someObject.subObject.deepSub is now "added"

console.log( monitor.someObject.subObject.anotherSub );
// fallback value of someObject.subObject.anotherSub is now "1000"

Relax monitoring

By default an object monitor instance is throwing exception when trying to change a previously changed property of monitored object without saving first. Using constructor arguments it is possible to permanently prevent this detection or replace the exception with a warning message logged on stderr.

Starting with v0.0.8 a new context method is available for disabling this detection temporarily, only.

const Monitor = require( "object-monitor" );

let data = {
    someObject: {
        subProperty: "its value",
    },
};

// create monitor on some data object
const monitor = Monitor( data, { recursive: true } );

console.log( monitor.someObject.subProperty ); // -> "its value"

// change once ...
monitor.someObject.subProperty = "new value";
console.log( monitor.someObject.subProperty ); // -> "new value"

// change again ...
monitor.someObject.subProperty = "newer value"; // THROWS!
console.log( monitor.someObject.subProperty ); // -> still "new value"

// relax
monitor.$context.relax();

// try changing again ...
monitor.someObject.subProperty = "newer value";
console.log( monitor.someObject.subProperty ); // -> "newer value"

// stop relaxing
monitor.$context.relax( false );

// change again ...
monitor.someObject.subProperty = "newest value"; // THROWS!
console.log( monitor.someObject.subProperty ); // -> still "newer value"

Cloning monitor

Starting with v0.8.0 you can create a clone of any monitored object resulting in a deep clone of monitored data which is observed by another monitor that's starting with the same list of existing changes.

const Monitor = require( "object-monitor" );

let data = {
    someObject: {
        subProperty: "its value",
    },
};

// create monitor on some data object
const monitor = Monitor( data, { recursive: true } );

console.log( monitor.someObject.subProperty ); // -> "its value"

// change once ...
monitor.someObject.subProperty = "new value";
console.log( monitor.someObject.subProperty ); // -> "new value"

// create a clone
const clone = monitor.$context.clone();

console.log( clone.someObject.subProperty ); // -> "new value"
console.log( clone.$context.hasChanged ); // -> true

// commit the clone
clone.$context.commit();

console.log( clone.someObject.subProperty ); // -> "new value"
console.log( clone.$context.hasChanged ); // -> false

console.log( monitor.someObject.subProperty ); // -> "new value"
console.log( monitor.$context.hasChanged ); // -> true

// roll back the source
monitor.$context.rollBack();

console.log( clone.someObject.subProperty ); // -> "new value"
console.log( clone.$context.hasChanged ); // -> false

console.log( monitor.someObject.subProperty ); // -> "its value"
console.log( monitor.$context.hasChanged ); // -> false

Custom comparison

Starting in v0.1, a custom callback may be provided to inspect complex data in detail:

const Monitor = require( "object-monitor" );

const data = {
    someObject: {
        subProperty: Buffer.from( "12345" ),
    },
};

const unmanaged = Monitor( data, {
    recursive: true,
} );

const managed = Monitor( data, {
    recursive: true,
    customCompare: ( previous, assigned ) => previous.equals( assigned ),
} );

// deeply assigning w/o custom comparison works fine for scalar data and for 
// assigning identical non-scalar data
unmanaged.someObject.subProperty = data.someObject.subProperty;
console.log( unmanaged.$context.hasChanged ); // -> false

// deeply assigning w/o custom comparison may be tracked as change though inner
// value hasn't changed actually
unmanaged.someObject.subProperty = Buffer.from( "12345" );
console.log( unmanaged.$context.hasChanged ); // -> true

// deeply assigning w/ custom comparison is capable of managing those cases
managed.someObject.subProperty = Buffer.from( "12345" );
console.log( managed.$context.hasChanged ); // -> false

Some limitations apply:

  • Custom comparison callback is invoked after applying any coercion.
  • No custom comparison callback is invoked on providing identical value.
  • The same applies if coercion of a given value results in identical value.

Custom separator

The object monitor is listing names of properties that have changed recently in its context. Due to recursively tracking properties of nested objects, those names represent hierarchical path names into the top-level set of data which is monitored. These names use . for separating levels of hierarchy by default. Thus, an exception is thrown whenever there is a property including . in its name.

Starting with v0.1, the separator used can be replaced by another single character to enable use of . in property naming.

const Monitor = require( "object-monitor" );

const data = {
    someObject: {
        subProperty: Buffer.from( "12345" ),
    },
};

const regular = Monitor( data, {
    recursive: true,
} );

const adjusted = Monitor( data, {
    recursive: true,
    separator: "/",
} );

regular["some.Object"] = 1; // throws
regular.someObject["sub.Property"] = 1; // throws
regular["some/Object"] = 1; // does not throw
regular.someObject["sub/Property"] = 1; // does not throw

adjusted["some.Object"] = 1; // does not throw
adjusted.someObject["sub.Property"] = 1; // does not throw
adjusted["some/Object"] = 1; // throws
adjusted.someObject["sub/Property"] = 1; // throws