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

custom-bitfield

v1.1.0

Published

A customisable bitfield that can be used for building e.g. your own permissions system (or similar). Works with any amount of bits.

Downloads

88

Readme

Custom Bitfield

This is a utility package that allows you to create your own bitfields for things such as permission management for efficient comparison and storage. It depends on bigints (123n notation), and therefore needs a runtime environment that supports ES2020 features:

  • For node.js projects, you'll need Node.js version 12.9.0 or later.
  • For Typescript projects, you'll need to set "target": "ES2020" or anything above ES2020 in your tsconfig.json.

Older versions of different internet browsers might not support this feature, please refer to https://caniuse.com/bigint for further information.

Usage:

1. Create a Flag Map

In order to create a valid flag map to use, all numbers in it need to be of type bigint, be powers of two, and all values in it need to be unique. The suggested approach is as follows:

const MyFlagMap = {
	AdminAccess: 1n << 0n,
	ReadAccess:  1n << 1n,
	WriteAccess: 1n << 2n,
	EditAccess:  1n << 3n,
	// ...
}

You can add however many bits / flags you need, however, please keep two things in mind:

[!NOTE] The amount of bitshifts you do, such as 1n << 9999n determines the size of your bitfield in bits. Starting your first entry with a bitshift of 9999 bits ensures that your bitfield is always at least 10'000 bits large. It therefore makes most sense to avoid large bitshifts and start with small ones first.

[!WARNING] Once you store and retrieve bits in a bitfield, any changes to the meanings or values of any existing flags will mess up all your previously stored values, as they will be interpreted according to the new flag map, not the one they were initialised with. Therefore, it makes most sense to only add new permissions to the end of the flag map and always choose a value that has never been used before. Never reassign values in the flag map, unless you know exactly what you are doing.

2. Create the Bitfield

A bitfield needs a valid flag map to be created. If the flag map is invalid, this step will throw an error.

const myBitField = new BitField(MyFlagMap);

3. Edit the Bitfield

Adding Bits to the Bitfield

myBitfield.add(MyFlagMap.EditAccess);
myBitfield.add(MyFlagMap.ReadAccess);
myBitfield.addAll([ MyFlagMap.EditAccess, MyFlagMap.ReadAccess ]);
myBitfield.addAllFromBitfield(someOtherBitfield);

Removing Bits from the Bitfield

myBitfield.remove(MyFlagMap.EditAccess);
myBitfield.removeAll([ MyFlagMap.EditAccess, MyFlagMap.AdminAccess ]);
myBitfield.removeAllFromBitfield(someOtherBitfield);
myBitfield.reset(); // removes ALL bits from the bitfield

4. Query and Compare the Bitfield

Check if the Bitfield Has Some or All Flags

myBitfield.has(MyFlagMap.EditAccess); // these functions all return a boolean
myBitfield.hasAll([ MyFlagMap.EditAccess, MyFlagMap.ReadAccess ]);
myBitfield.hasAllFromBitfield(someOtherBitfield);
myBitfield.hasAny([ MyFlagMap.EditAccess, MyFlagMap.AdminAccess ]);
myBitfield.hasAnyFromBitfield(someOtherBitfield);

Turn the Bitfield Into an Array of Flags

const foundFlags = myBitfield.toArray(); // returns a bigint[]
if (foundFlags.includes(MyFlagMap.AdminAccess)) console.log("Has Admin Access.");
if (foundFlags.includes(MyFlagMap.ReadAccess))  console.log("Has Read Access.");
if (foundFlags.includes(MyFlagMap.WriteAccess)) console.log("Has Write Access.");
if (foundFlags.includes(MyFlagMap.EditAccess))  console.log("Has Edit Access.");

Find Which Flags Specified in the Function Parameter Are Missing From This Bitfield

// these all return a bigint[] of all missing flags.
// Assuming our bitfield has EditAccess and ReadAccess, we get the following results:
myBitfield.missing(MyFlagMap.AdminAccess); // [ MyFlagMap.AdminAccess ]
myBitfield.missing(MyFlagMap.EditAccess); // []
myBitfield.missingFrom([ MyFlagMap.ReadAccess, MyFlagMap.AdminAccess ]); // [ MyFlagMap.AdminAccess ]
myBitfield.missingFromBitfield(someOtherBitfield);

5. Serialise and Deserialise (Store and Load) the Bitfield

const stringToStore = myBitfield.toString();

// some storing and loading functionality...
// const loadedBitField = database.load(...)

// Important! Use same flag map as for the stored values!
const myNewBitField = new BitField(MyFlagMap);

try { // Unsafe conversion.
	myNewBitField.fromStringUnsafe(loadedBitField);
} catch (error) {
	console.log('Loaded Bit Field was not a number.', error);
}

try { // Safe conversion.
	myNewBitField.fromStringSafe(loadedBitField);
} catch (error) {
	// String was empty, not a number, a negative number,
	// or had bits set that are not specified in the flag map.
	console.log(error);
}

// Safety checks:
if (myNewBitField.isValidValue(loadedBitField)) {
	try {
		myNewBitField.fromStringUnsafe(loadedBitField);
	} catch (error) {
		console.log('This catch should never happen.', error);
	}
} else {
	// String was empty, not a number, a negative number,
	// or had bits set that are not specified in the flag map.
}

if (myNewBitField.isValidValueSoft(loadedBitField)) {
	try {
		myNewBitField.fromStringUnsafe(loadedBitField);
	} catch (error) {
		console.log('This catch should never happen.', error);
	}
} else {
	// String was not a number or a negative number.
}

6. Advanced Usage:

In case you wish to use the bitfield with bigints instead of your predefined flags and other bitfields, this is possible as well.

myBitfield.add(0b1110n); // Set the flags for Read, Write and Edit Access.
myBitfield.addAll([ 0b1000n, 0b1100n, 0b0101n ]); // Equivalent: myBitfield.add(0b1101)

myBitfield.remove(0b0010n); // Remove the flag for Read Access. Equivalent: myBitfield.remove(2n)
myBitfield.removeAll([ 0b0011n, 0b0001n ]); // Equivalent: myBitfield.remove(0b0011)

myBitfield.has(0b1100n); // Are ALL the bits present, i.e. do we have Write AND Edit access?
myBitfield.hasAll([ 0b1100n, 0b0010n ]); // Equivalent: myBitfield.has(0b1110n)

myBitfield.anyBits(0b0101n); // Are ANY bits present, i.e. do we have Admin OR Write access?
myBitfield.hasAny([ 0b0100n, 0b0011n ]); // Equivalent: myBitfield.anyBits(0b0111n)

myBitfield.missing(0b1111n); // Which bits set in 0b1111n are not set in the bitfield?
myBitfield.missing(0b1100n); // Which bits set in 0b1100n are not set in the bitfield?

(As you'll have larger bitfields, it might make sense to use decimal or hexadecimal bigint notation.)

If you like this library (and can spare a coin) consider to ko-fi