diff --git a/lib/permissions/minimally-encoded-thread-permissions.js b/lib/permissions/minimally-encoded-thread-permissions.js --- a/lib/permissions/minimally-encoded-thread-permissions.js +++ b/lib/permissions/minimally-encoded-thread-permissions.js @@ -7,6 +7,7 @@ import type { ThreadPermission, ThreadPermissionsInfo, + ThreadRolePermissionsBlob, } from '../types/thread-permission-types.js'; import { entries } from '../utils/objects.js'; @@ -87,22 +88,49 @@ 'child_opentoplevel_': BigInt(1) << BigInt(11), }); -const rolePermissionToBitmaskHex = (permissionString: string): string => { - const parsedPermissionString: ParsedThreadPermissionString = - parseThreadPermissionString(permissionString); - +const rolePermissionToBitmask = ( + parsedPermissionString: ParsedThreadPermissionString, +): // TODO (atul): Update flow to `194.0.0` for bigint support +// $FlowIssue bigint-unsupported +bigint => { const { propagationPrefix, filterPrefix } = parsedPermissionString; const lookupKey = `${propagationPrefix ?? ''}${filterPrefix ?? ''}`; invariant( lookupKey in permissionVariantBit, `lookupKey must be in permissionVariantBit`, ); - return permissionVariantBit[lookupKey].toString(16); + return permissionVariantBit[lookupKey]; +}; + +const rolePermissionsToBitmaskHex = ( + threadRolePermissions: ThreadRolePermissionsBlob, +): string => { + // TODO (atul): Update flow to `194.0.0` for bigint support + // $FlowIssue bigint-unsupported + const permissionsMap = new Map(); + for (const permission of Object.keys(threadRolePermissions)) { + const parsed = parseThreadPermissionString(permission); + permissionsMap.set( + parsed.permission, + (permissionsMap.get(parsed.permission) ?? BigInt(0)) | + BigInt(rolePermissionToBitmask(parsed)), + ); + } + + let bitmask = BigInt(0); + Object.keys(minimallyEncodedThreadPermissions).forEach((permission, idx) => { + let permissionBitmask = permissionsMap.get(permission) ?? BigInt(0); + permissionBitmask <<= BigInt(idx) * BigInt(12); + bitmask |= permissionBitmask; + }); + + return bitmask.toString(16); }; export { minimallyEncodedThreadPermissions, permissionsToBitmaskHex, hasPermission, - rolePermissionToBitmaskHex, + rolePermissionToBitmask, + rolePermissionsToBitmaskHex, }; diff --git a/lib/permissions/minimally-encoded-thread-permissions.test.js b/lib/permissions/minimally-encoded-thread-permissions.test.js --- a/lib/permissions/minimally-encoded-thread-permissions.test.js +++ b/lib/permissions/minimally-encoded-thread-permissions.test.js @@ -3,8 +3,11 @@ import { hasPermission, permissionsToBitmaskHex, - rolePermissionToBitmaskHex, + rolePermissionsToBitmaskHex, + rolePermissionToBitmask, } from './minimally-encoded-thread-permissions.js'; +import { parseThreadPermissionString } from './prefixes.js'; +import type { ThreadRolePermissionsBlob } from '../types/thread-permission-types.js'; describe('minimallyEncodedThreadPermissions', () => { const permissions = { @@ -53,39 +56,179 @@ }); }); +// TODO (atul): Update flow to `194.0.0` for bigint support +// $FlowIssue bigint-unsupported describe('rolePermissionToBitmaskHex', () => { it('should encode variants of `know_of` as bitmask', () => { - expect(rolePermissionToBitmaskHex(`know_of`)).not.toBe(1); - expect(rolePermissionToBitmaskHex(`know_of`)).not.toBe('0'); - expect(rolePermissionToBitmaskHex(`know_of`)).not.toBe(''); - expect(rolePermissionToBitmaskHex(`know_of`)).toBe('1'); + expect( + rolePermissionToBitmask(parseThreadPermissionString(`know_of`)), + ).not.toBe(1); + expect( + rolePermissionToBitmask(parseThreadPermissionString(`know_of`)), + ).not.toBe('0'); + expect( + rolePermissionToBitmask(parseThreadPermissionString(`know_of`)), + ).not.toBe(''); + expect( + rolePermissionToBitmask(parseThreadPermissionString(`know_of`)), + ).toBe( + // $FlowIssue bigint-unsupported + 1n, + ); - expect(rolePermissionToBitmaskHex(`descendant_know_of`)).toBe('2'); - expect(rolePermissionToBitmaskHex(`child_know_of`)).toBe('4'); - expect(rolePermissionToBitmaskHex(`open_know_of`)).toBe('8'); - expect(rolePermissionToBitmaskHex(`toplevel_know_of`)).toBe('10'); - expect(rolePermissionToBitmaskHex(`opentoplevel_know_of`)).toBe('20'); - expect(rolePermissionToBitmaskHex(`descendant_open_know_of`)).toBe('40'); - expect(rolePermissionToBitmaskHex(`descendant_toplevel_know_of`)).toBe( - '80', + expect( + rolePermissionToBitmask( + parseThreadPermissionString(`descendant_know_of`), + ), + ).toBe( + // $FlowIssue bigint-unsupported + 2n, + ); + expect( + rolePermissionToBitmask(parseThreadPermissionString(`child_know_of`)), + ).toBe( + // $FlowIssue bigint-unsupported + 4n, + ); + expect( + rolePermissionToBitmask(parseThreadPermissionString(`open_know_of`)), + ).toBe( + // $FlowIssue bigint-unsupported + 8n, + ); + expect( + rolePermissionToBitmask(parseThreadPermissionString(`toplevel_know_of`)), + ).toBe( + // $FlowIssue bigint-unsupported + 16n, + ); + expect( + rolePermissionToBitmask( + parseThreadPermissionString(`opentoplevel_know_of`), + ), + ).toBe( + // $FlowIssue bigint-unsupported + 32n, + ); + expect( + rolePermissionToBitmask( + parseThreadPermissionString(`descendant_open_know_of`), + ), + ).toBe( + // $FlowIssue bigint-unsupported + 64n, + ); + expect( + rolePermissionToBitmask( + parseThreadPermissionString(`descendant_toplevel_know_of`), + ), + ).toBe( + // $FlowIssue bigint-unsupported + 128n, + ); + expect( + rolePermissionToBitmask( + parseThreadPermissionString(`descendant_opentoplevel_know_of`), + ), + ).toBe( + // $FlowIssue bigint-unsupported + 256n, + ); + expect( + rolePermissionToBitmask( + parseThreadPermissionString(`child_open_know_of`), + ), + ).toBe( + // $FlowIssue bigint-unsupported + 512n, ); - expect(rolePermissionToBitmaskHex(`descendant_opentoplevel_know_of`)).toBe( - '100', + expect( + rolePermissionToBitmask( + parseThreadPermissionString(`child_toplevel_know_of`), + ), + ).toBe( + // $FlowIssue bigint-unsupported + 1024n, ); - expect(rolePermissionToBitmaskHex(`child_open_know_of`)).toBe('200'); - expect(rolePermissionToBitmaskHex(`child_toplevel_know_of`)).toBe('400'); - expect(rolePermissionToBitmaskHex(`child_opentoplevel_know_of`)).toBe( - '800', + expect( + rolePermissionToBitmask( + parseThreadPermissionString(`child_opentoplevel_know_of`), + ), + ).toBe( + // $FlowIssue bigint-unsupported + 2048n, ); }); it('should fail to encode invalid variants of `know_of` as bitmask', () => { - expect(() => rolePermissionToBitmaskHex(`ancestor_know_of`)).toThrow( - 'string is not threadPermissions enum', + expect(() => + rolePermissionToBitmask(parseThreadPermissionString(`ancestor_know_of`)), + ).toThrow('string is not threadPermissions enum'); + + expect(() => + rolePermissionToBitmask( + parseThreadPermissionString(`announcement_know_of`), + ), + ).toThrow('string is not threadPermissions enum'); + }); +}); + +// TODO (atul): Update flow to `194.0.0` for bigint support +// $FlowIssue bigint-unsupported +describe('rolePermissionsToBitmaskHex', () => { + const permissionsA: ThreadRolePermissionsBlob = { + add_members: true, + child_open_join_thread: true, + create_sidebars: true, + create_subthreads: true, + descendant_open_know_of: true, + descendant_open_visible: true, + descendant_opentoplevel_join_thread: true, + edit_entries: true, + edit_message: true, + edit_permissions: true, + edit_thread: true, + edit_thread_avatar: true, + edit_thread_color: true, + edit_thread_description: true, + know_of: true, + leave_thread: true, + react_to_message: true, + remove_members: true, + visible: true, + voiced: true, + open_know_of: true, + open_visible: true, + opentoplevel_join_thread: true, + toplevel_know_of: true, + toplevel_visible: true, + opentoplevel_know_of: true, + opentoplevel_visible: true, + child_know_of: true, + child_visible: true, + child_opentoplevel_join_thread: true, + child_toplevel_know_of: true, + child_toplevel_visible: true, + child_opentoplevel_know_of: true, + child_opentoplevel_visible: true, + }; + + const permissionsB = { + ...permissionsA, + child_join_thread: true, + }; + + it('should successfully encode permissionsA to bitmask', () => { + const bitmaskA = rolePermissionsToBitmaskHex(permissionsA); + expect(bitmaskA).toBe( + '1001001001000001001001b20001001000001001001001001c7dc7d', ); + }); - expect(() => rolePermissionToBitmaskHex(`announcement_know_of`)).toThrow( - 'string is not threadPermissions enum', + it('should successfully encode permissionsB to bitmask', () => { + const bitmaskB = rolePermissionsToBitmaskHex(permissionsB); + expect(bitmaskB).toBe( + '1001001001000001001001b24001001000001001001001001c7dc7d', ); }); });