A tiny utility to make using bitwise operations readable by mere mortals. Drink up!
npm install flagon --save
Use either a chaining API
flag(A).method(B).method(B).value() or access the raw functions via
var permission =READ: 0b0001CREATE: 0b0010MODIFY: 0b0100DELETE: 0b1000//Raw APIvar GUEST = permissionREADvar USER = flagonvar ADMIN = flagonvar SUPERUSER = flagonvar SUPER_GUEST = flagon//Chaining APIvar DELETE_BUT_NOT_READ =DELETE_BUT_NOT_READ == falseDELETE_BUT_NOT_READ == true
Why make something so concise so verbose?
Bitwise operations are very powerful and concise. But exactly because they are so powerful, they are often used in security critical situations. By making the operations more readable it is less likely the developer will unintentionally permit access to restricted data or operations.
I developed this utility for validating user and organizations permissions in a REST API. For simpler use cases, you may prefer to stick to using the underlying bitwise operations.
Why is Binary OR named merge? and XOR named toggle?
This utility is trying to infer the purpose of the operation in the context of your application. It is likely this guess may be incorrect, and if so I'd be happy to alias the function names to anything reasonable.
Here is an example where you may be using Binary OR to
var permission =READ: 0b0001CREATE: 0b0010MODIFY: 0b0100DELETE: 0b1000var GUEST = permissionREADvar USER = flagonvar ADMIN = flagonvar SUPERUSER = flagon
And you may want to check if a
And you may want to temporarily grant access to
MODIFY to a
var GUEST_THAT_CAN_MODIFY = flagon//then revoke it latervar GUEST = flagon
All of the examples seek to demonstrate that the operations purpose is more important than the underlying binary flags. The flags themselves are uninteresting, but they facilitate expansion of your permission model without altering other aspects of your application (e.g. your DB Schema)
merge (Binary OR)
A | B
Merges all true flags that share a column in two binary sequences.
0b1000 | 0b0100 | 0b0010 == 0b1110flagonvalue == 0b1110
B == 0 || ((A & B) == B)
Does a bit mask contain every true value of another bit mask?
var A = 0b1000var B = 0b1100;A & B == B == falseflagon == == false;A & B == A == true
contains will automatically unwrap a wrapped value.
(A ^ B)
Flips every bit of the object that has a different true value to the subject.
var A = 0b1000var B = 0b1100;A ^ B == 0b0100flagon == 0b0100//toggling twice reverts the changevalue == A == true
flagon(A) + ""
Outputs a binary represenation of a number as a string.
== +"" == 10
4 == "100"
You can still access this functionality via flagon by simply calling
toString(2) on the outputted value.
flagon(4).value().toString(2) == 100 //output Hexadecimal too! flagon(15).value().toString(16) == 'f'
Outputs the number value A that was wrapped by calling
flagon(A). Useful for extracting the number value after performing a chained operation.
Wraps a value so you can call a series of operations. Access the raw value by calling
value() or access a binary representation by calling
//flagon(A).<method>(B).<method>(C).value()//toString== "11"//or+"" == "11"//valuevalue == 0b101
I don't really like chaining, it doesn't work well with composition. But I added chaining when writing the library to help me get my head around the operations. One benefit of chaining, is it becomes quite clear which argument is the object, and which is the subject. In your actual application code I don't see it being that useful, but I also don't see any need for removing it when the whole script is ~20 lines of code.
Where is bit shifting?
I didn't need it for my use case, YAGNI. PR's welcome though.
In this readme there are often chained equalities e.g.
2 == (1 + 1) == (4 / 2)
2 == 1 + 1 == 4 / 2 true == 2 true
But that is only because 2 evaluates to a truthy value.
I'll change this in future so it is less confusing.