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

dytracker

v0.2.1

Published

Dytracker is simple library ment to enable diff of objects using a provided blueprint

Downloads

3

Readme

Dytracker

NPM version

Dy(namic) tracker is simple library ment to enable diff of objects using a provided blueprint

Example

import { Dytracker } from 'dytracker'

const tracker = new Dytracker({
  name: true,
  email: true,
  permission: {
    id: true,
  },
  posts: {
    keyname: 'id', // To enable tracking of lists, array elements will need an unique identifier
    tracking: {
      title: true,
    },
  },
})

const user = {
  id: 1, // It is required that the top level object have an unique identifier
  name: 'Jane Doe',
  email: '[email protected]',
  permission: {
    id: 1,
    name: 'Admin', // Properties not in the blueprint will simply be ignored
  },
  posts: [
    {
      id: 1,
      title: 'Hi there',
    },
  ],
}

tracker.track(user)

// now lets change `user`
user.name = 'Jane Smith'
user.permission.id = 2
user.posts.push({ id: 2, title: 'Follow me at github!' })

tracker.diff(user)
/*
  {
  name: 'Jane Smith',
  permission: { id: 2 },
  posts: {
    added: [ { id: 2, title: 'Follow me at github!' } ],
    removed: [],
    updated: []
  }
}
*/

// Stop tracking user
tracker.flush(user.id)

Typescript

Dytracker was written in typescript so it has first class support for types. The same example above can be written as:

import { Dytracker } from 'dytracker'

interface User {
  id: number
  name: string
  email: string
  permission: {
    id: number
    name: string
  }
  posts: Array<{
    id: number
    title: string
  }>
}

/* Providing a generic will enable suggestions for the blueprint object */
const tracker = new Dytracker<User>({
  name: true,
  email: true,
  permission: {
    id: true,
  },
  posts: {
    keyname: 'id',
    tracking: {
      title: true,
    },
  },
})

const user: User = {
  id: 1,
  name: 'Jane Doe',
  email: '[email protected]',
  permission: {
    id: 1,
    name: 'Admin',
  },
  posts: [
    {
      id: 1,
      title: 'Hi there',
    },
  ],
}

tracker.track(user)

user.name = 'Jane Smith'
user.permission.id = 2
user.posts.push({ id: 2, title: 'Follow me at github!' })

/*
  The type of the diff return will basically be a Partial of the generic you provided
  with the exception of arrays, which will be objects like { added: T[], updated: T[], removed: T[] }
*/
tracker.diff(user)
/*
  {
  name: 'Jane Smith',
  permission: { id: 2 },
  posts: {
    added: [ { id: 2, title: 'Follow me at github!' } ],
    removed: [],
    updated: []
  }
}
*/

// Stop tracking user
tracker.flush(user.id)

API

Top level object

Dytracker enables you to monitor changes to any property of an object using the basic, nested or list interfaces. The top level object can have the folowing options:

_keyname: keyof T

By default the top level object is tracked using the id property. _keyname allows you to change the property used to track the object.

interface Obj {
  key: string
  foo: number
}

const tracker = new Dytracker<Obj>({
  _keyname: 'key',
  foo: true,
})

Basic properties

This is the first building block of Dytracker, it makes all properties that map to primitives selectable with a boolean.

There are no other options to the basic interface

interface Obj {
  id: number
  foo: number
}

const tracker = new Dytracker<Obj>({
  foo: true,
})

Nested properties

When you need to track an object nested inside your top level object you will need to use the nested interface, which is a recursive interface that enables basic and nested types to an object. Therefore you can select properties of a nested object with a boolean and nested objects with the same API.

interface User {
  id: number
  name: string
  permission: {
    id: number
    name: string
  }
}

const tracker = new Dytracker<User>({
  name: true,
  permission: {
    id: true,
    name: true,
  },
})

_predicate: (a: T, b: T) => boolean

Somethimes you don't need to monitor properties of a nested object, especially for value objects. In that case you can use _predicate to dictate how dytracker will check for equality of that object.

Caveats:
  • Because you define the predicate for equality, the object being checked will be deep cloned using structuredClone
  • When _predicate is provided all other options will be ignored e.g. you cannot watch nested properties of an object you will compare manually.
interface Obj {
  id: number
  createdAt: Date
}

const tracker = new Dytracker<Obj>({
  createdAt: {
    _predicate: (a, b) => a.getTime() === b.getTime(),
  },
})

List properties

Dytracker makes it easy to keep track of what was added, updated and removed from a list, this is especially usefull when you have 1-N relationship mapped into your object. To do that, your array needs to consist of objects with a unique identifier property, usually id, if you have that, you can make use the list API to track elements of an array and even choose what you want to track in each element.

Caveats:
  • It's currently not possible to track lists of primitives likes number or string
  • The only supported list is Array
interface Post {
  id: number
  title: string
}

interface Obj {
  id: number
  posts: Post[]
}

const tracker = new Dytracker<Obj>({
  posts: {
    keyname: 'id',
    tracking: {
      title: true,
    },
  },
})