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

@bradleymeck/compositekey

v1.0.0

Published

This proposal seeks to add APIs to create composite keys while still allowing the components of the composite key to be GC'd.

Downloads

6

Readme

compositeKey, compositeSymbol

This proposal seeks to add APIs to create composite keys while still allowing the components of the composite key to be GC'd.

API

In all APIs order of arguments is preserved in the path to the key. compositeKey(a, b) is different from compositeKey(b, a).

compositeKey requires at least one component must be a valid key that can be placed in a WeakMap . This is because the main use case for compositeKey is to allow GC to occur when the lifetime of the components is ended. compositeSymbol is for strongly putting the key on an Object and does not benefit from this.

compositeKey(...parts: [...any]) : Object.freeze({__proto__:null})

compositeSymbol(...parts: [...any]) : Symbol()

Where will it live

A builtin module; how to import builtin modules TBD based upon current TC39 discussions.

FAQ

Why have both compositeKey and compositeSymbol?

They are serving two slightly different use cases.

compositeKey

Allows using a Map/Set/WeakMap to weakly and/or privately associate data with the lifetime of a group of values.

compositeSymbol

Allows strongly attaching data to an Object that is associtated with a group of values. This API can be roughly recreated by using:

let symbols = new WeakMap;
compositeSymbol = (...parts) => {
  const key = compositeKey(...parts);
  if (!symbols.has(key)) symbols.set(key, Symbol());
  return symbols.get(key);
}

However, this causes a problem of not being a global cache like Symbol.for or compositeKey and may cause fragmentation. It also would be ideal to have compositeSymbol act like Symbol.for in order to reduce total number of possible entries being held onto.

Why a frozen empty Object for compositeKey?

So that properties cannot be added to the object that will leak to the global or can be used as a public side channel.

This gives a few constraints:

  1. The return value must be frozen and a frozen prototype to prevent the side channel from being able to be obtained purely off the reference itself. This leads to null being a good choice for the prototype.

  2. This constraint must be applied to all properties of the object. While no properties are planned for the return value, the values of properties should follow these rules and/or be a primitive.

Why require a lifetime?

This prevents accidental leakage by always ensuring keys have a lifetime associated with them.

What scope is the idempotentcy?

Still up for debate but some TC39 members would like it to be per Realm.

Having it be per Realm allows the key store to be more granular and free up segments as Realms are GC'd, but means that there could be multiple keys that correspond to compositeKey(A, B) if you obtain multiple compositeKey instances from multiple realms.

Having it be across Realms means that you cannot cause duplicate keys for component parts, and matches with Symbol.for. Since the result of compositeKey has a null prototype there is not a way to distinguish which Realm the result was first created in.

Currently, this proposal is looking to progress with cross Realm idempotentcy.

When can the key be GC'd

The path to a key in the key store can be GC'd once any owner of a lifetime in the path is GC'd. This means once any single non-primitive component is GC'd the key cannot be obtained again using these APIs. The key itself is an object subject to normal GC rules and will be removed when it is no longer strongly held.

If you only store keys in WeakMaps the key and associated values can be GC'd as soon a component of the key is GC'd.

If you store keys in strongly held structues like a Map; the key will not be able to be GC'd since the key could still be obtained inspecting map.keys() which could be used to get the value associated with the key.

How could I create a composite key that can return its components?

You can create a Map that strongly preserves your components in order:

const myValues = new Map();

const components = [a, b];
const myKey = compositeKey(...components);
myValues.set(myKey, components);


// ...

let [a, b] = myValues.get(myKey);