A lightweight TypeScript library for managing permissions using bitmasks. Using just utility functions will be even smaller with tree-shaking.
Bitmasks are a way to store multiple boolean values in a single integer. They are useful for managing permissions, flags or groups.
For example in UNIX file systems, bitmasks are used to manage file permissions (read, write, execute) for users, groups and others. Like 777 for full access to everyone, 755 for read and execute access to everyone, but full access to the owner.
- Fast: Bitwise operations (&, |) are faster than comparing strings.
- Compact: Combine multiple permissions in a single integer (e.g., 0b1111 for read, create, update, delete). Just 4 bits for access control. For groups, you can use any number of bits,
- Flexible: Easy to check, add, or remove permissions.
Example of using bitmasks:
const READ = 1; // 0b0001
const CREATE = 2; // 0b0010
const UPDATE = 4; // 0b0100
const DELETE = 8; // 0b1000
const userPermissions = READ | CREATE | UPDATE; // 0b0111
const canRead = (userPermissions & READ) === READ; // true
const canCreate = (userPermissions & CREATE) === CREATE; // true
const canUpdate = (userPermissions & UPDATE) === UPDATE; // true
const canDelete = (userPermissions & DELETE) === DELETE; // false[ Group (0–29 bits) | Permissions (4 bits) ]
0b0001_0111 = 23
\__/ \__/
/ \
Group(1) Permissions(read, create, update)
Install permask:
# npm
npm install permask
# pnpm
pnpm add permask
# yarn
yarn add permaskTry out permask interactively! Live Demo
// examples
// with object
const PermissionGroup = {
POST: 1,
COMMENT: 2,
LIKE: 3
} as const;import { createPermask } from "permask";
import { PermissionGroup } from "./permission-group"; // your defined groups
const permask = createPermask(PermissionGroup);const bitmask2 = permask.create({
group: "LIKE",
read: true,
create: false,
update: true,
delete: false
});
console.log(bitmask2); // 53 (0b110101)const parsed = permask.parse(31); // 0b11111
console.log(parsed);
// {
// group: 1,
// groupName: "POST",
// read: true,
// create: true,
// update: true,
// delete: true
// }const hasGroup = permask.hasGroup(23, "LIKE");
console.log(hasGroup); // true
// You can also use numeric group IDs
const hasGroupById = permask.hasGroup(23, PermissionGroup.LIKE);const canRead = permask.canRead(17);
const canCreate = permask.canCreate(17);
const canDelete = permask.canDelete(17);
const canUpdate = permask.canUpdate(17);
console.log(canRead, canCreate, canDelete, canUpdate); // true, false, false, falseimport { PermissionAccess } from "permask";
// Check if bitmask has read access to LIKE group using string access
const hasReadAccess = permask.hasAccess(53, "LIKE", "read");
console.log(hasReadAccess); // true
// Check if bitmask has create access to LIKE group using string access
const hasCreateAccess = permask.hasAccess(53, "LIKE", "create");
console.log(hasCreateAccess); // false
// You can also use numeric group IDs with string access
const hasAccessById = permask.hasAccess(53, PermissionGroup.LIKE, "update");
console.log(hasAccessById); // true
// You can use numeric access values (PermissionAccess) instead of strings
const hasUpdateAccessNumeric = permask.hasAccess(53, PermissionGroup.LIKE, PermissionAccess.UPDATE);
console.log(hasUpdateAccessNumeric); // trueconst groupName = permask.getGroupName(23);
console.log(groupName); // "LIKE"
const groupName2 = permask.getGroupName(29);
console.log(groupName2); // undefinedYou can use permask just with bitmask utility functions.
But it will be without some types dependent on your groups.
Functions:
-
createBitmask({ group: number, read: boolean, create: boolean, delete: boolean, update: boolean }): number- creates a bitmask from an options. -
parseBitmask(bitmask: number): { group: number, read: boolean, create: boolean, delete: boolean, update: boolean }- parses a bitmask and returns an object. -
getPermissionGroup(bitmask: number): number- returns a group number from a bitmask. -
getPermissionAccess(bitmask: number): number- returns an access number from a bitmask. -
hasPermissionGroup(bitmask: number, group: number): boolean- checks if a bitmask has a specific group. -
hasPermissionAccess(bitmask: number, access: number): boolean- checks if a bitmask has a specific access. -
hasRequiredPermission(bitmasks: number[], group: number, access: number): boolean- checks if any bitmask in the array has the required permission for the specified group and access.useful functions:
canRead(bitmask: number): booleancanCreate(bitmask: number): booleancanDelete(bitmask: number): booleancanUpdate(bitmask: number): boolean
-
setPermissionGroup(bitmask: number, group: number): number- sets a group in a bitmask (will overwrite the previous group). -
setPermissionAccess(bitmask: number, access: number): number- sets access in a bitmask (will overwrite the previous access). -
getPermissionBitmask(group: number, access: number): number- creates a bitmask from a group and access. -
packBitbasks(bitmasks: number[], urlSafe?: boolean): string- packs bitmasks to base64 string. (more compact than JSON.stringify) -
unpackBitmasks(base64: string, urlSafe?: boolean): number[]- unpacks bitmasks from a base64 string.
Constants:
PermissionAccess- an enum-like object with access types.const PermissionAccess = { READ: 1, // 0b0001 CREATE: 2, // 0b0010 UPDATE: 4, // 0b0100 DELETE: 8 // 0b1000 } as const;
PermissionAccessBitmasks- full access bitmask for usual cases.const PermissionAccessBitmasks = { FULL: 0b1111, // read, create, update, delete CREATE: 0b0011, // read, create READ: 0b0001 // read-only } as const;
I'm using permask in my projects to manage permissions for users. It's easy to use and understand. And that's why I decided to share it with you.
For example, I'm storing bitmask permissions array in access tokens for users. It's easy to check if user has access to a specific functionality or group.
It's possible to store ~820 bitmask permissions(1 group + 3 access) in 1kB. In JS - 128 bitmasks, because each number in JS weights 4bytes
With strings like Posts.Read, Users.Create it will be just ~35 permissions (1 group + 1 access)
If you have any questions or suggestions, feel free to open an issue or pull request.
- Create a library
- Add tests
- Add documentation
- Add easy-to-use integration with frameworks
- Express
- Fastify
- H3
- Nitro
- NestJS
- Hono
- Koa
- itty-router
Copyright (c) 2025 by Dmytro Shevchenko