UNPKG

@piyawasin/byte-flags

Version:

A lightweight, type-safe utility library for efficiently storing and managing boolean flags in compact numeric values (8, 16, or 32 bits). Perfect for permission systems, feature flags, status tracking, and any scenario requiring compact boolean storage w

366 lines (365 loc) 13.5 kB
"use strict"; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseFlags = void 0; /** * Abstract base class for efficiently storing and managing boolean flags in a numeric value. * This provides the common functionality for ByteFlags, ShortFlags, and LongFlags. */ var BaseFlags = /** @class */ (function () { // ===== CONSTRUCTOR ===== /** * Create a new flags instance with the specified flag names * @param flagNames Names of flags to initialize * @throws Error if no flag names are provided or if flag names are invalid */ function BaseFlags() { var flagNames = []; for (var _i = 0; _i < arguments.length; _i++) { flagNames[_i] = arguments[_i]; } // ===== PROTECTED PROPERTIES ===== /** The internal value storing all flags */ this.value = 0; /** Current index for the next flag to be added */ this.nextIndex = 0; /** List of all flag names in order of creation */ this.flagList = []; /** Mapping of flag names to their bit positions */ this.flagIndices = new Map(); if (flagNames.length === 0) { throw new Error("At least one flag name must be provided"); } // Note: Max flags validation must be done in subclass constructors // since we can't access abstract properties here // Add each flag to the instance for (var _a = 0, flagNames_1 = flagNames; _a < flagNames_1.length; _a++) { var name = flagNames_1[_a]; this.addFlag(name); } } // ===== PROTECTED METHODS ===== /** * Add a new flag to this instance * @param name Name of the flag to add * @throws Error if more than maximum number of flags are added or if flag name is invalid */ BaseFlags.prototype.addFlag = function (name) { var _this = this; if (this.nextIndex >= this.MAX_FLAGS) { throw new Error("Cannot add more than ".concat(this.MAX_FLAGS, " flags")); } if (typeof name !== "string" || name.trim() === "") { throw new Error("Flag name must be a non-empty string"); } if (this.flagList.includes(name)) { throw new Error("Duplicate flag: ".concat(name)); } var idx = this.nextIndex++; this.flagList.push(name); this.flagIndices.set(name, idx); // Define property getters/setters for direct access (flags.isAdmin) Object.defineProperty(this, name, { enumerable: true, configurable: false, get: function () { return _this.getBit(idx); }, set: function (value) { return _this.setBit(idx, value); }, }); }; /** * Get the value of a bit at the specified position * @param position Bit position * @returns Boolean value of the bit */ BaseFlags.prototype.getBit = function (position) { return Boolean(this.value & (1 << position)); }; /** * Set the value of a bit at the specified position * @param position Bit position * @param value Boolean value to set */ BaseFlags.prototype.setBit = function (position, value) { if (value) { this.value |= 1 << position; } else { this.value &= ~(1 << position); } }; // ===== PUBLIC METHODS: FLAG OPERATIONS ===== /** * Check if a flag exists in this instance * @param name The name of the flag to check * @returns True if the flag exists, false otherwise */ BaseFlags.prototype.hasFlag = function (name) { return this.flagIndices.has(name); }; /** * Get the value of a flag by name * @param name The name of the flag to get * @returns The boolean value of the flag * @throws Error if the flag doesn't exist */ BaseFlags.prototype.getFlag = function (name) { if (!this.hasFlag(name)) { throw new Error("Flag '".concat(name, "' does not exist")); } var idx = this.flagIndices.get(name); return this.getBit(idx); }; /** * Set the value of a flag by name * @param name The name of the flag to set * @param value The boolean value to set * @returns This instance for chaining * @throws Error if the flag doesn't exist */ BaseFlags.prototype.setFlag = function (name, value) { if (!this.hasFlag(name)) { throw new Error("Flag '".concat(name, "' does not exist")); } var idx = this.flagIndices.get(name); this.setBit(idx, value); return this; }; /** * Toggle a flag's value * @param name The name of the flag to toggle * @returns This instance for chaining * @throws Error if the flag doesn't exist */ BaseFlags.prototype.toggleFlag = function (name) { if (!this.hasFlag(name)) { throw new Error("Flag '".concat(name, "' does not exist")); } var idx = this.flagIndices.get(name); this.setBit(idx, !this.getBit(idx)); return this; }; /** * Set multiple flags at once * @param flags Object with flag names as keys and boolean values * @returns This instance for chaining */ BaseFlags.prototype.setFlags = function (flags) { for (var _i = 0, _a = Object.entries(flags); _i < _a.length; _i++) { var _b = _a[_i], name = _b[0], value = _b[1]; if (this.hasFlag(name)) { this.setFlag(name, value); } } return this; }; /** * Toggle multiple flags at once * @param names Names of the flags to toggle * @returns This instance for chaining */ BaseFlags.prototype.toggleFlags = function () { var names = []; for (var _i = 0; _i < arguments.length; _i++) { names[_i] = arguments[_i]; } for (var _a = 0, names_1 = names; _a < names_1.length; _a++) { var name = names_1[_a]; if (this.hasFlag(name)) { this.toggleFlag(name); } } return this; }; /** * Get the number of flags currently set to true * @returns Count of set flags */ BaseFlags.prototype.count = function () { var count = 0; for (var i = 0; i < this.nextIndex; i++) { if (this.getBit(i)) { count++; } } return count; }; /** * Check if any flag is set to true * @returns True if any flag is true */ BaseFlags.prototype.any = function () { return this.value !== 0; }; /** * Check if no flags are set to true * @returns True if all flags are false */ BaseFlags.prototype.none = function () { return this.value === 0; }; /** * Convert the flags to a numeric value * @returns Numeric representation of all flags */ BaseFlags.prototype.toValue = function () { return this.value; }; /** * Load flags from a numeric value * @param v Value to load * @returns This instance for chaining * @throws Error if the value is invalid */ BaseFlags.prototype.fromValue = function (v) { if (!Number.isInteger(v) || v < 0 || v > this.MAX_VALUE) { throw new Error("Value must be integer 0-".concat(this.MAX_VALUE)); } this.value = v; return this; }; /** * Convert the flags to a plain JavaScript object * @returns Object with flag names as keys and boolean values */ BaseFlags.prototype.toObject = function () { var _this = this; return this.flagList.reduce(function (obj, name) { obj[name] = _this.getFlag(name); return obj; }, {}); }; /** * Convert the flags to a JSON string * @returns JSON string representation of the flags */ BaseFlags.prototype.toJSON = function () { return JSON.stringify(this.toObject()); }; /** * Check if all specified flags are set to true * @param flags Flag names to check * @returns True if all specified flags are true */ BaseFlags.prototype.all = function () { var _this = this; var flags = []; for (var _i = 0; _i < arguments.length; _i++) { flags[_i] = arguments[_i]; } return flags.every(function (flag) { return _this.getFlag(flag); }); }; /** * Check if any of the specified flags are set to true * @param flags Flag names to check * @returns True if any specified flag is true */ BaseFlags.prototype.anyOf = function () { var _this = this; var flags = []; for (var _i = 0; _i < arguments.length; _i++) { flags[_i] = arguments[_i]; } return flags.some(function (flag) { return _this.getFlag(flag); }); }; /** * Check if none of the specified flags are set to true * @param flags Flag names to check * @returns True if no specified flags are true */ BaseFlags.prototype.noneOf = function () { var flags = []; for (var _i = 0; _i < arguments.length; _i++) { flags[_i] = arguments[_i]; } return !this.anyOf.apply(this, flags); }; /** * Get an array of all flag names * @returns Array of all flag names in order of creation */ BaseFlags.prototype.getFlagNames = function () { return __spreadArray([], this.flagList, true); }; /** * Get an iterator for all flags as [name, value] pairs * @returns Iterator yielding [name, boolean] tuples */ BaseFlags.prototype[Symbol.iterator] = function () { var _i, _a, name; return __generator(this, function (_b) { switch (_b.label) { case 0: _i = 0, _a = this.flagList; _b.label = 1; case 1: if (!(_i < _a.length)) return [3 /*break*/, 4]; name = _a[_i]; return [4 /*yield*/, [name, this.getFlag(name)]]; case 2: _b.sent(); _b.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/]; } }); }; /** * Get a string representation of the flags * @returns String in the format "FlagsType {flag1=true, flag2=false, ...}" */ BaseFlags.prototype.toString = function () { var flags = Array.from(this) .map(function (_a) { var name = _a[0], value = _a[1]; return "".concat(name, "=").concat(value); }) .join(", "); return "".concat(this.constructor.name, " {").concat(flags, "}"); }; /** * @deprecated Use getFlagNames() instead for better clarity * @returns Array of all flag names */ BaseFlags.prototype.getFlags = function () { return this.getFlagNames(); }; return BaseFlags; }()); exports.BaseFlags = BaseFlags;