Define once — the shared file both ends import
import { definePermissions } from "@tahanabavi/type-permission";
export const P = definePermissions({
chat: {
VIEW_CHANNEL: { bit: 0 },
SEND_MESSAGES: { bit: 1, requires: ["chat.VIEW_CHANNEL"] },
MANAGE_MESSAGES: { bit: 2, implies: ["chat.SEND_MESSAGES"] },
ATTACH_FILES: { bit: 3, requires: ["chat.SEND_MESSAGES"] },
},
guild: {
MANAGE_ROLES: { bit: 8 },
KICK_MEMBERS: { bit: 9 },
ADMINISTRATOR: { bit: 63, grantsAll: true },
},
});Nested modules yield the same "module.member" id shape as endpointId
(typefetch) and eventId (typesocket). bit is explicit and permanent —
duplicate bits throw at load, and a bigint means there is no 64-flag ceiling.
Check — typo-proof
P.has(perms, "chat.SEND_MESSAGES"); // a typo is a compile error, not `false`
P.hasAll(perms, ["chat.SEND_MESSAGES", "chat.ATTACH_FILES"]);
P.list(perms); // typed ("chat.VIEW_CHANNEL" | …)[]
P.explain(perms, "chat.SEND_MESSAGES");
// { granted: false, reason: "denied-requires",
// detail: "chat.SEND_MESSAGES is gated by chat.VIEW_CHANNEL, which was denied." }