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

@agile-ts/proxytree

v0.0.10

Published

Create Proxy Tree based on the accessed properties

Downloads

31

Readme

[INTERNAL] ProxyTree

Create Proxy Tree based on the accessed properties

❓ What is it for?

The Proxy Tree is an internal library of AgileTs. It is used to wrap a proxy around a target object, and its nested objects as you access them in order to keep track of which properties were accessed via get/has proxy handlers. This allows AgileTs to restrict paths to these accessed properties. With these paths, AgileTs can optimize the rerender count of Components by only rendering them when an (in the Component) accessed property mutates.

▶️ Use case in AgileTs

For example, this functionality is used in the useProxy() hook, which is used to subscribe a State to a React Component.

// MyComponent.whatever
const myObject = useProxy(MY_OBJECT);

return <p>{myObject.data.adressData.street}</p>;

The Component (represented in the above example) is only rerendered by AgileTs if myObject.data.adressData.street mutates and no longer when anything in the object changes. (like it is in useAgile()) For instance, if we change myObject.data.name the Component won't be rerendered, since this property isn't accessed in it.

🌳 Construction of Proxy Tree

First, the main target object is converted into the root Branch (.rootBranch). Each Branch represents a particular object wrapped in a Proxy() and keeps track of its child Branches. These child Branches are created as soon as a certain property in the parent Branch has been accessed. Then the accessed property will be transformed into a sub Branch of the parent Branch and gets part of the Proxy Tree. This way, only accessed properties are added to the Proxy Tree. The Proxy Tree isn't aware of the other (not accessed) properties, as they aren't yet relevant. As soon as an unrecognized property is accessed, it is added to the Proxy Tree.

// Orginal Object with sub Objects
const original = {
  a: [{ b: 1 }, { 1000: { a: { b: 1 } } }, '3rd'],
  b: { c: { d: 'hi' } },
  c: { a: 'hi' },
};

// Create Proxy Tree
const proxyTree = new ProxyTree(original);
const proxyfiedOrginal = proxyTree.proxy;  

// Access Properties
proxyfiedOrginal.a;
proxyfiedOrginal.a[0];
proxyfiedOrginal.c.a;

// Proxy Tree looks like that:
Branch({
  a: Branch([Branch({ b: 1 }), { 1000: { a: { b: 1 } } }, '3rd']),
  b: { c: { d: 'hi' } },
  c: Branch({ a: Branch('hi') }),
});

// Access more Properties
proxyfiedOrginal.b;
proxyfiedOrginal.a[2];

// Proxy Tree now looks like that:
Branch({
  a: Branch([Branch({ b: 1 }), { 1000: { a: { b: 1 } } }, Branch('3rd')]),
  b: Branch({ c: { d: 'hi' } }),
  c: Branch({ a: Branch('hi') }),
});

⚡️ API

getUsedRoutes()

Returns the Paths to the accessed properties in array shape.

// Orginal Object with sub Objects
const original = {
  a: [{ b: 1 }, { 1000: { a: { b: 1 } } }, '3rd'],
  b: { c: { d: 'hi' } },
  c: { a: 'hi' },
};

// Create Proxy Tree
const proxyTree = new ProxyTree(original);
const proxyfiedOrginal = proxyTree.proxy;

// Access Properties
proxyfiedOrginal.a;
proxyfiedOrginal.a[0]['b'];
proxyfiedOrginal.a[1][1000]['a']['b'];
proxyfiedOrginal.c.a;
proxyfiedOrginal.b;
proxyfiedOrginal.a;

// Get route to accessed Properties
console.log(proxyTree.getUsedRoutes()); // Returns (see below)
// [
//   ['a', '0', 'b'],
//   ['a', '1', '1000', 'a', 'b'],
//   ['c', 'a'],
//   ['b'],
//   ['a'],
// ]

The algorithm behind reconstructing the used routes/paths is pretty straightforward and simple. It may not be very efficient, but it works, and that is what counts for now.

In the above image, each blue-circled property is an end accessed property (so b and x). Each time a property was accessed, the Proxy Tree counted the used property of this Route/Node. Also, between Routes like 'a' or 'c' were accessed and thus incremented. This is due the fact that, by accessing the property c via a.b.c, you access a and b before accessing c. This way, we are able to reconstruct the paths to the accessed properties with the simple algorithm you can find in the above image.

transformTreeToBranchObject()

Transforms Proxy Tree into an easily processable object. Therefore, it goes through each Branch (starting at the root Branch) and transforms them into BranchObjects.

// Orginal Object with sub Objects
const original = {
  a: [{ b: 1 }, { 1000: { a: { b: 1 } } }, '3rd'],
  b: { c: { d: 'hi' } },
  c: { a: 'hi' },
};

// Create Proxy Tree
const proxyTree = new ProxyTree(original);
const proxyfiedOrginal = proxyTree.proxy;

// Access Properties
proxyfiedOrginal.a;
proxyfiedOrginal.a[0];
proxyfiedOrginal.c.a;

console.log(proxyTree.transformTreeToBranchObject()); // Returns (see below)
// {
//   key: 'root',
//   timesAccessed: 3,
//   branches: [
//     {
//       key: 'a',
//       timesAccessed: 2,
//       branches: [{ key: '0', timesAccessed: 1, branches: [] }],
//     },
//     {
//       key: 'c',
//       timesAccessed: 1,
//       branches: [{ key: 'a', timesAccessed: 1, branches: [] }],
//     },
//   ],
// }

📄 Documentation

Sounds AgileTs interesting to you? Checkout our documentation, to learn more. And I promise you, you will be able to use AgileTs in no time. If you have any further questions, don't hesitate to join our Community Discord.

⭐️ Contribute

Get a part of AgileTs and start contributing. We welcome any meaningful contribution. 😀 To find out more about contributing, check out the CONTRIBUTING.md.