diff --git a/.husky/pre-commit b/.husky/pre-commit index d0a7784..6064714 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -npx lint-staged \ No newline at end of file +npx lint-staged && node --test ./tests/ \ No newline at end of file diff --git a/src/commands/handler.ts b/src/commands/handler.ts index a649f41..b9833e7 100644 --- a/src/commands/handler.ts +++ b/src/commands/handler.ts @@ -484,11 +484,11 @@ export class CommandHandler extends BaseHandler { option.onPermissionsFail?.bind(option) ?? commandInstance.onPermissionsFail?.bind(commandInstance) ?? this.client.options.commands?.defaults?.onPermissionsFail; - option.botPermissions = PermissionsBitField.add( + option.botPermissions = new PermissionsBitField().add( option.botPermissions ?? PermissionsBitField.None, commandInstance.botPermissions, ); - option.defaultMemberPermissions ??= PermissionsBitField.add( + option.defaultMemberPermissions ??= new PermissionsBitField().add( option.defaultMemberPermissions ?? PermissionsBitField.None, commandInstance.defaultMemberPermissions, ); diff --git a/src/structures/extra/BitField.ts b/src/structures/extra/BitField.ts index 4ad5706..15de184 100644 --- a/src/structures/extra/BitField.ts +++ b/src/structures/extra/BitField.ts @@ -18,21 +18,6 @@ export class BitField { return this.bit; } - add(...bits: (BitFieldResolvable | undefined)[]): bigint { - for (const bit of bits) { - if (!bit) continue; - this.bit |= this.resolve(bit); - } - return this.bit; - } - - remove(...bits: BitFieldResolvable[]): bigint { - for (const bit of bits) { - this.bit &= ~this.resolve(bit); - } - return this.bit; - } - has(...bits: BitFieldResolvable[]) { const bitsResolved = bits.map(bit => this.resolve(bit)); return bitsResolved.every(bit => (this.bits & bit) === bit); @@ -47,10 +32,6 @@ export class BitField { return this.bits === this.resolve(other); } - resolve(bits: BitFieldResolvable): bigint { - return BitField.resolve(bits); - } - keys(bits: BitFieldResolvable[] = [this.bits]) { const bitsResolved = bits.map(bit => this.resolve(bit)); return Object.entries(this.Flags).reduce((acc, value) => { @@ -73,27 +54,25 @@ export class BitField { }, [] as bigint[]); } - static add(base: BitFieldResolvable, ...bits: (BitFieldResolvable | undefined)[]) { - base = BitField.resolve(base); - + add(...bits: (BitFieldResolvable | undefined)[]) { for (const bit of bits) { if (!bit) continue; - base |= BitField.resolve(bit); + this.bits |= this.resolve(bit); } - return base; + return this.bits; } - static remove(base: BitFieldResolvable, ...bits: BitFieldResolvable[]): bigint { - base = BitField.resolve(base); - + remove(...bits: BitFieldResolvable[]): bigint { for (const bit of bits) { - base &= ~BitField.resolve(bit); + this.bits &= ~this.resolve(bit); } - return base; + return this.bits; } - static resolve(bits: BitFieldResolvable): bigint { + resolve(bits: BitFieldResolvable): bigint { switch (typeof bits) { + case 'string': + return this.resolve(this.Flags[bits]); case 'number': return BigInt(bits); case 'bigint': @@ -102,10 +81,12 @@ export class BitField { if (!Array.isArray(bits)) { throw new TypeError(`Cannot resolve permission: ${bits}`); } - return bits.map(x => BitField.resolve(x)).reduce((acc, cur) => acc | cur, BitField.None); + return bits.map(x => this.resolve(x)).reduce((acc, cur) => acc | cur, BitField.None); } default: - throw new TypeError(`Cannot resolve permission: ${typeof bits === 'symbol' ? String(bits) : (bits as any)}`); + throw new TypeError( + `Cannot resolve permission: ${typeof bits === 'symbol' ? String(bits) : (bits as unknown)}`, + ); } } } diff --git a/src/structures/extra/Permissions.ts b/src/structures/extra/Permissions.ts index 03173ac..09519b8 100644 --- a/src/structures/extra/Permissions.ts +++ b/src/structures/extra/Permissions.ts @@ -8,7 +8,7 @@ export class PermissionsBitField extends BitField { constructor(bitfields?: BitFieldResolvable) { super(); - if (bitfields) this.bit = PermissionsBitField.resolve(bitfields); + if (bitfields) this.bit = this.resolve(bitfields); } declare keys: (bits?: BitFieldResolvable[]) => PermissionStrings; @@ -21,12 +21,26 @@ export class PermissionsBitField extends BitField { return super.has(...bits); } - static resolve(bits: BitFieldResolvable): bigint { + resolve(bits: BitFieldResolvable): bigint { + return PermissionsBitField.resolve(bits); + } + + static resolve(bits: BitFieldResolvable): bigint { switch (typeof bits) { case 'string': return PermissionsBitField.resolve(PermissionFlagsBits[bits as keyof typeof PermissionFlagsBits]); + case 'number': + return BigInt(bits); + case 'bigint': + return bits; + case 'object': { + if (!Array.isArray(bits)) { + throw new TypeError(`Cannot resolve permission: ${bits}`); + } + return bits.map(x => PermissionsBitField.resolve(x)).reduce((acc, cur) => acc | cur, BitField.None); + } default: - return BitField.resolve(bits); + throw new TypeError(`Cannot resolve permission: ${typeof bits === 'symbol' ? String(bits) : (bits as any)}`); } } } diff --git a/tests/bitfield.test.js b/tests/bitfield.test.js new file mode 100644 index 0000000..9898525 --- /dev/null +++ b/tests/bitfield.test.js @@ -0,0 +1,67 @@ +//@ts-check +const { describe, test } = require('node:test'); +const { PermissionsBitField } = require('../lib/structures/extra/Permissions'); +const assert = require('node:assert/strict'); +const { PermissionFlagsBits } = require('../lib/types'); +const { BitField } = require('../lib/structures/extra/BitField'); + +describe('PermissionsBitField', () => { + test('constructor', () => { + const p = new PermissionsBitField(['CreateEvents']); + assert.equal(p.bits, PermissionFlagsBits.CreateEvents); + }); + + test('add', () => { + const p = new PermissionsBitField(['CreateEvents']); + p.add(['AttachFiles']); + p.add('ChangeNickname'); + assert.equal( + p.bits, + PermissionFlagsBits.CreateEvents | PermissionFlagsBits.AttachFiles | PermissionFlagsBits.ChangeNickname, + ); + }); + + test('remove', () => { + const p = new PermissionsBitField(['CreateEvents']); + p.remove('CreateEvents'); + assert.equal(p.bits, BitField.None); + }); + + test('keys', () => { + const p = new PermissionsBitField(['CreateEvents', 'Administrator']); + p.add(['AttachFiles']); + p.add('ChangeNickname'); + + const keys = ['CreateEvents', 'Administrator', 'AttachFiles', 'ChangeNickname']; + assert.equal( + true, + p.keys().every(x => keys.includes(x)), + ); + assert.equal(p.keys().length, 4); + }); + + test('values', () => { + const p = new PermissionsBitField(['CreateEvents']); + p.add('Administrator'); + p.add(['ChangeNickname']); + assert.deepEqual(p.values(), [ + PermissionFlagsBits.Administrator, + PermissionFlagsBits.ChangeNickname, + PermissionFlagsBits.CreateEvents, + ]); + }); + + test('missings', () => { + const p = new PermissionsBitField(['CreateEvents']); + p.add('Administrator'); + p.add(['ChangeNickname']); + assert.deepEqual(p.missings('Connect'), [PermissionFlagsBits.Connect]); + }); + + test('equals', () => { + const p = new PermissionsBitField(['CreateEvents']); + p.add(['ChangeNickname']); + assert.deepEqual(p.equals(['ChangeNickname', 'CreateEvents']), true); + assert.deepEqual(p.equals('Connect'), false); + }); +});