UNPKG

@shield-acl/react

Version:

Sistema ACL (Access Control List) inteligente e granular para aplicações React

1,259 lines (1,243 loc) 39 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { ACLProvider: () => ACLProvider, Can: () => Can, CanAll: () => CanAll, CanAny: () => CanAny, Cannot: () => Cannot, IfCan: () => IfCan, PermissionClass: () => PermissionClass, useACL: () => useACL, useACLContext: () => useACLContext, useAvailableActions: () => useAvailableActions, useAvailableResources: () => useAvailableResources, useCan: () => useCan, useCanAll: () => useCanAll, useCanAllActions: () => useCanAllActions, useCanAny: () => useCanAny, useCanAnyAction: () => useCanAnyAction, useCanArray: () => useCanArray, useCanMap: () => useCanMap, useCanMultiple: () => useCanMultiple, useCannot: () => useCannot, useCombinedConditionalPermission: () => useCombinedConditionalPermission, useConditionalChecker: () => useConditionalChecker, useConditionalPermission: () => useConditionalPermission, useConditionalPermissions: () => useConditionalPermissions, useEvaluate: () => useEvaluate, useEvaluationResult: () => useEvaluationResult, useHasRole: () => useHasRole, useMultiplePermissionChange: () => useMultiplePermissionChange, useMultiplePermissionStates: () => useMultiplePermissionStates, useMultipleResourceACL: () => useMultipleResourceACL, usePermissionAnalysis: () => usePermissionAnalysis, usePermissionBoolean: () => usePermissionBoolean, usePermissionChange: () => usePermissionChange, usePermissionChangeDetector: () => usePermissionChangeDetector, usePermissionEffect: () => usePermissionEffect, usePermissionHelpers: () => usePermissionHelpers, usePermissionState: () => usePermissionState, usePermissions: () => usePermissions, usePermissionsList: () => usePermissionsList, useResourceACL: () => useResourceACL, useResourcePermissions: () => useResourcePermissions, useRoleHierarchy: () => useRoleHierarchy, useRoleInfo: () => useRoleInfo, withCan: () => withCan }); module.exports = __toCommonJS(src_exports); // src/context.tsx var import_react = require("react"); var import_jsx_runtime = require("react/jsx-runtime"); var ACLContext = (0, import_react.createContext)(void 0); function ACLProvider({ engine, user: initialUser = null, children }) { const [user, setUser] = (0, import_react.useState)(initialUser); const can = (0, import_react.useCallback)( (action, resource, context) => { if (!user) return false; return engine.can(user, action, resource, context); }, [engine, user] ); const evaluate = (0, import_react.useCallback)( (action, resource, context) => { if (!user) { return { allowed: false, reason: "No user authenticated" }; } return engine.evaluate(user, action, resource, context); }, [engine, user] ); const value = (0, import_react.useMemo)( () => ({ engine, user, setUser, can, evaluate }), [engine, user, can, evaluate] ); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ACLContext.Provider, { value, children }); } function useACLContext() { const context = (0, import_react.useContext)(ACLContext); if (!context) { throw new Error("useACLContext must be used within an ACLProvider"); } return context; } // src/hooks/useACL.ts var import_react2 = require("react"); function useACL() { const { engine, user, setUser, can, evaluate } = useACLContext(); const cannot = (0, import_react2.useCallback)( (action, resource, context) => { return !can(action, resource, context); }, [can] ); const permissions = (0, import_react2.useCallback)(() => { if (!user) return []; return engine.getUserPermissions(user); }, [engine, user]); const roles = (0, import_react2.useCallback)(() => { if (!user) return []; return user.roles; }, [user]); const clearCache = (0, import_react2.useCallback)(() => { engine.clearCache(); }, [engine]); return { // Estado user, setUser, // Verificações can, cannot, evaluate, // Dados permissions, roles, // Engine engine, // Utilidades clearCache }; } // src/hooks/useCan.ts var import_react3 = require("react"); function useCan(action, resource, context) { const { can, user } = useACLContext(); return (0, import_react3.useMemo)(() => { return can(action, resource, context); }, [can, action, resource, context, user]); } function useCannot(action, resource, context) { const canResult = useCan(action, resource, context); return !canResult; } // src/hooks/useCanAny.ts var import_react4 = require("react"); function useCanAny(permissions) { const { can, user } = useACLContext(); return (0, import_react4.useMemo)(() => { if (!permissions || permissions.length === 0) { return false; } return permissions.some( (permission) => can(permission.action, permission.resource, permission.context) ); }, [can, permissions, user]); } function useCanAnyAction(actions) { const permissions = (0, import_react4.useMemo)( () => actions.map((action) => ({ action })), [actions] ); return useCanAny(permissions); } // src/hooks/useCanAll.ts var import_react5 = require("react"); function useCanAll(permissions) { const { can, user } = useACLContext(); return (0, import_react5.useMemo)(() => { if (!permissions || permissions.length === 0) { return true; } return permissions.every( (permission) => can(permission.action, permission.resource, permission.context) ); }, [can, permissions, user]); } function useCanAllActions(actions) { const permissions = (0, import_react5.useMemo)( () => actions.map((action) => ({ action })), [actions] ); return useCanAll(permissions); } // src/hooks/usePermissionHelpers.ts var import_react6 = require("react"); function usePermissionHelpers() { const { can, user } = useACLContext(); const canRead = (0, import_react6.useCallback)( (resource, context) => { return can(`${resource}.read`, resource, context) || can("read", resource, context) || can(`${resource}.view`, resource, context) || can("view", resource, context); }, [can] ); const canCreate = (0, import_react6.useCallback)( (resource, context) => { return can(`${resource}.create`, resource, context) || can("create", resource, context) || can(`${resource}.add`, resource, context) || can("add", resource, context); }, [can] ); const canUpdate = (0, import_react6.useCallback)( (resource, context) => { return can(`${resource}.update`, resource, context) || can("update", resource, context) || can(`${resource}.edit`, resource, context) || can("edit", resource, context) || can(`${resource}.modify`, resource, context) || can("modify", resource, context); }, [can] ); const canDelete = (0, import_react6.useCallback)( (resource, context) => { return can(`${resource}.delete`, resource, context) || can("delete", resource, context) || can(`${resource}.remove`, resource, context) || can("remove", resource, context); }, [can] ); const canManage = (0, import_react6.useCallback)( (resource, context) => { return can(`${resource}.manage`, resource, context) || can("manage", resource, context) || can(`${resource}.*`, resource, context) || can("*", resource, context) || // Se tem todas as permissões CRUD individuais, também pode gerenciar canRead(resource, context) && canCreate(resource, context) && canUpdate(resource, context) && canDelete(resource, context); }, [can, canRead, canCreate, canUpdate, canDelete] ); const canList = (0, import_react6.useCallback)( (resource, context) => { return can(`${resource}.list`, resource, context) || can("list", resource, context) || canRead(resource, context); }, [can, canRead] ); const canView = (0, import_react6.useCallback)( (resource, context) => { return can(`${resource}.view`, resource, context) || can("view", resource, context) || canRead(resource, context); }, [can, canRead] ); const canEdit = (0, import_react6.useCallback)( (resource, context) => { return can(`${resource}.edit`, resource, context) || can("edit", resource, context) || canUpdate(resource, context); }, [can, canUpdate] ); const canModify = (0, import_react6.useCallback)( (resource, context) => { return can(`${resource}.modify`, resource, context) || can("modify", resource, context) || canUpdate(resource, context); }, [can, canUpdate] ); const canAccess = (0, import_react6.useCallback)( (resource, context) => { return can(`${resource}.access`, resource, context) || can("access", resource, context) || canRead(resource, context) || canCreate(resource, context) || canUpdate(resource, context) || canDelete(resource, context); }, [can, canRead, canCreate, canUpdate, canDelete] ); return (0, import_react6.useMemo)( () => ({ canRead, canCreate, canUpdate, canDelete, canManage, canList, canView, canEdit, canModify, canAccess }), [ canRead, canCreate, canUpdate, canDelete, canManage, canList, canView, canEdit, canModify, canAccess, user ] ); } function useResourcePermissions(resource) { const helpers = usePermissionHelpers(); return (0, import_react6.useMemo)( () => ({ canRead: (context) => helpers.canRead(resource, context), canCreate: (context) => helpers.canCreate(resource, context), canUpdate: (context) => helpers.canUpdate(resource, context), canDelete: (context) => helpers.canDelete(resource, context), canManage: (context) => helpers.canManage(resource, context), canList: (context) => helpers.canList(resource, context), canView: (context) => helpers.canView(resource, context), canEdit: (context) => helpers.canEdit(resource, context), canModify: (context) => helpers.canModify(resource, context), canAccess: (context) => helpers.canAccess(resource, context) }), [helpers, resource] ); } // src/hooks/usePermissions.ts var import_react7 = require("react"); function usePermissions() { const { engine, user } = useACLContext(); return (0, import_react7.useMemo)(() => { if (!user) { return { all: [], direct: [], inherited: [], byRole: {}, denials: [], allowances: [], actions: [], resources: [], count: 0 }; } const allPermissions = engine.getUserPermissions(user); const directPermissions = user.permissions || []; const inheritedPermissions = allPermissions.filter( (perm) => !directPermissions.includes(perm) ); const permissionsByRole = {}; user.roles.forEach((roleName) => { const role = engine.getRole(roleName); if (role) { permissionsByRole[roleName] = role.permissions; } }); const denials = allPermissions.filter((perm) => perm.deny === true); const allowances = allPermissions.filter((perm) => perm.deny !== true); const uniqueActions = /* @__PURE__ */ new Set(); allPermissions.forEach((perm) => { if (Array.isArray(perm.action)) { perm.action.forEach((action) => uniqueActions.add(action)); } else { uniqueActions.add(perm.action); } }); const uniqueResources = /* @__PURE__ */ new Set(); allPermissions.forEach((perm) => { if (perm.resource) { if (Array.isArray(perm.resource)) { perm.resource.forEach((resource) => uniqueResources.add(resource)); } else { uniqueResources.add(perm.resource); } } }); return { all: allPermissions, direct: directPermissions, inherited: inheritedPermissions, byRole: permissionsByRole, denials, allowances, actions: Array.from(uniqueActions).sort(), resources: Array.from(uniqueResources).sort(), count: allPermissions.length }; }, [engine, user]); } function usePermissionsList() { const { all } = usePermissions(); return all; } function useAvailableActions() { const { actions } = usePermissions(); return actions; } function useAvailableResources() { const { resources } = usePermissions(); return resources; } // src/hooks/useCanMultiple.ts var import_react8 = require("react"); function useCanMultiple(permissions, keys) { const { can, evaluate, user } = useACLContext(); return (0, import_react8.useMemo)(() => { const results = {}; const details = {}; const allowed = []; const denied = []; permissions.forEach((permission, index) => { const key = (keys == null ? void 0 : keys[index]) || `${permission.action}${permission.resource ? `.${permission.resource}` : ""}`; const evaluationResult = evaluate( permission.action, permission.resource, permission.context ); const isAllowed = evaluationResult.allowed; results[key] = isAllowed; details[key] = evaluationResult; if (isAllowed) { allowed.push(permission); } else { denied.push(permission); } }); const allowedCount = allowed.length; const deniedCount = denied.length; const allAllowed = allowedCount === permissions.length; const anyAllowed = allowedCount > 0; const noneAllowed = allowedCount === 0; return { allAllowed, anyAllowed, noneAllowed, allowedCount, deniedCount, results, details, allowed, denied }; }, [can, evaluate, permissions, keys, user]); } function useCanMap(permissions, keys) { const { results } = useCanMultiple(permissions, keys); return results; } function useCanArray(permissions) { const { can, user } = useACLContext(); return (0, import_react8.useMemo)(() => { return permissions.map( (permission) => can(permission.action, permission.resource, permission.context) ); }, [can, permissions, user]); } // src/hooks/useResourceACL.ts var import_react9 = require("react"); function useResourceACL(resource) { const { can: canCheck, user } = useACLContext(); const helpers = usePermissionHelpers(); const canRead = (0, import_react9.useCallback)( (context) => helpers.canRead(resource, context), [helpers, resource] ); const canCreate = (0, import_react9.useCallback)( (context) => helpers.canCreate(resource, context), [helpers, resource] ); const canUpdate = (0, import_react9.useCallback)( (context) => helpers.canUpdate(resource, context), [helpers, resource] ); const canDelete = (0, import_react9.useCallback)( (context) => helpers.canDelete(resource, context), [helpers, resource] ); const canManage = (0, import_react9.useCallback)( (context) => helpers.canManage(resource, context), [helpers, resource] ); const canList = (0, import_react9.useCallback)( (context) => helpers.canList(resource, context), [helpers, resource] ); const canView = (0, import_react9.useCallback)( (context) => helpers.canView(resource, context), [helpers, resource] ); const canEdit = (0, import_react9.useCallback)( (context) => helpers.canEdit(resource, context), [helpers, resource] ); const canModify = (0, import_react9.useCallback)( (context) => helpers.canModify(resource, context), [helpers, resource] ); const canAccess = (0, import_react9.useCallback)( (context) => helpers.canAccess(resource, context), [helpers, resource] ); const can = (0, import_react9.useCallback)( (action, context) => canCheck(action, resource, context), [canCheck, resource] ); const cannot = (0, import_react9.useCallback)( (action, context) => !canCheck(action, resource, context), [canCheck, resource] ); const crudPermissions = useCanMultiple( [ { action: `${resource}.read`, resource }, { action: `${resource}.create`, resource }, { action: `${resource}.update`, resource }, { action: `${resource}.delete`, resource }, { action: `${resource}.manage`, resource } ], ["read", "create", "update", "delete", "manage"] ); const hasAnyPermission = crudPermissions.anyAllowed; const hasAllCrudPermissions = crudPermissions.results.read && crudPermissions.results.create && crudPermissions.results.update && crudPermissions.results.delete; const hasReadOnlyAccess = crudPermissions.results.read && !crudPermissions.results.create && !crudPermissions.results.update && !crudPermissions.results.delete; const hasWriteAccess = crudPermissions.results.create || crudPermissions.results.update || crudPermissions.results.delete; const hasFullAccess = crudPermissions.results.manage || hasAllCrudPermissions; const availableActions = (0, import_react9.useMemo)(() => { const actions = []; const standardActions = [ "read", "list", "view", "create", "add", "new", "update", "edit", "modify", "delete", "remove", "manage", "*" ]; standardActions.forEach((action) => { if (canCheck(`${resource}.${action}`, resource) || canCheck(action, resource)) { actions.push(`${resource}.${action}`); } }); return [...new Set(actions)].sort(); }, [canCheck, resource, user]); return (0, import_react9.useMemo)( () => ({ resource, // Funções CRUD canRead, canCreate, canUpdate, canDelete, canManage, // Aliases canList, canView, canEdit, canModify, canAccess, // Verificação customizada can, cannot, // Status de permissões permissions: { read: crudPermissions.results.read, create: crudPermissions.results.create, update: crudPermissions.results.update, delete: crudPermissions.results.delete, manage: crudPermissions.results.manage }, // Helpers de status hasAnyPermission, hasAllCrudPermissions, hasReadOnlyAccess, hasWriteAccess, hasFullAccess, // Ações disponíveis availableActions }), [ resource, canRead, canCreate, canUpdate, canDelete, canManage, canList, canView, canEdit, canModify, canAccess, can, cannot, crudPermissions, hasAnyPermission, hasAllCrudPermissions, hasReadOnlyAccess, hasWriteAccess, hasFullAccess, availableActions ] ); } function useMultipleResourceACL(resources) { const acls = resources.map((resource) => ({ resource, acl: useResourceACL(resource) })); return (0, import_react9.useMemo)(() => { const map = {}; acls.forEach(({ resource, acl }) => { map[resource] = acl; }); return map; }, [acls]); } // src/hooks/usePermissionState.ts var import_react10 = require("react"); function usePermissionState(action, resource, context, options = {}) { const { can, user } = useACLContext(); const { refreshInterval = 0, onChange, enabled = true, initialValue = false } = options; const [state, setState] = (0, import_react10.useState)({ allowed: initialValue, loading: true, error: null, lastChecked: 0, refresh: () => { } }); const checkPermission = (0, import_react10.useCallback)(() => { if (!enabled) return; try { const newValue = can(action, resource, context); const now = Date.now(); setState((prev) => { if (prev.allowed !== newValue && !prev.loading && onChange) { onChange(newValue, prev.allowed); } return { allowed: newValue, loading: false, error: null, lastChecked: now, refresh: prev.refresh }; }); } catch (error) { setState((prev) => ({ ...prev, loading: false, error, lastChecked: Date.now() })); } }, [can, action, resource, context, enabled, onChange]); (0, import_react10.useEffect)(() => { checkPermission(); }, [checkPermission, user]); (0, import_react10.useEffect)(() => { if (refreshInterval > 0 && enabled) { const interval = setInterval(checkPermission, refreshInterval); return () => clearInterval(interval); } }, [refreshInterval, checkPermission, enabled]); (0, import_react10.useEffect)(() => { setState((prev) => ({ ...prev, refresh: checkPermission })); }, [checkPermission]); return state; } function useMultiplePermissionStates(permissions, options = {}) { const { can, user } = useACLContext(); const { refreshInterval = 0, enabled = true, initialValue = false } = options; const [states, setStates] = (0, import_react10.useState)(() => { const initial = {}; permissions.forEach(({ key }) => { initial[key] = { allowed: initialValue, loading: true, error: null, lastChecked: 0, refresh: () => { } }; }); return initial; }); const checkAllPermissions = (0, import_react10.useCallback)(() => { if (!enabled) return; const newStates = {}; permissions.forEach(({ action, resource, context, key }) => { try { const allowed = can(action, resource, context); newStates[key] = { allowed, loading: false, error: null, lastChecked: Date.now(), refresh: () => { } }; } catch (error) { newStates[key] = { allowed: false, loading: false, error, lastChecked: Date.now(), refresh: () => { } }; } }); setStates((prev) => { Object.keys(newStates).forEach((key) => { if (prev[key]) { newStates[key].refresh = prev[key].refresh; } }); return newStates; }); }, [can, permissions, enabled]); (0, import_react10.useEffect)(() => { checkAllPermissions(); }, [checkAllPermissions, user]); (0, import_react10.useEffect)(() => { if (refreshInterval > 0 && enabled) { const interval = setInterval(checkAllPermissions, refreshInterval); return () => clearInterval(interval); } }, [refreshInterval, checkAllPermissions, enabled]); (0, import_react10.useEffect)(() => { setStates((prev) => { const updated = { ...prev }; Object.keys(updated).forEach((key) => { updated[key] = { ...updated[key], refresh: checkAllPermissions }; }); return updated; }); }, [checkAllPermissions]); return states; } function usePermissionBoolean(action, resource, context) { const state = usePermissionState(action, resource, context); return [state.allowed, state.loading, state.refresh]; } // src/hooks/useConditionalPermissions.ts var import_react11 = require("react"); function useConditionalPermissions(permissions) { const { can, user } = useACLContext(); return (0, import_react11.useMemo)(() => { return permissions.map((perm) => { var _a; const conditionMet = typeof perm.condition === "function" ? perm.condition() : perm.condition; if (!conditionMet) { return { allowed: (_a = perm.defaultValue) != null ? _a : true, // Por padrão, permite se condição não for atendida conditionMet: false, skipped: true, reason: "Condition not met - permission check skipped" }; } const allowed = can(perm.action, perm.resource, perm.context); return { allowed, conditionMet: true, skipped: false, reason: allowed ? "Condition met and permission granted" : "Condition met but permission denied" }; }); }, [permissions, can, user]); } function useConditionalPermission(condition, action, resource, context, defaultValue) { const results = useConditionalPermissions([ { condition, action, resource, context, defaultValue } ]); return results[0]; } function useConditionalChecker() { const { can } = useACLContext(); return (0, import_react11.useCallback)( (condition, action, resource, context, defaultValue = true) => { const conditionMet = typeof condition === "function" ? condition() : condition; if (!conditionMet) { return { allowed: defaultValue, conditionMet: false, skipped: true, reason: "Condition not met - permission check skipped" }; } const allowed = can(action, resource, context); return { allowed, conditionMet: true, skipped: false, reason: allowed ? "Condition met and permission granted" : "Condition met but permission denied" }; }, [can] ); } function useCombinedConditionalPermission(mode, conditions, action, resource, context, defaultValue = true) { const combinedCondition = (0, import_react11.useMemo)(() => { const evaluatedConditions = conditions.map( (c) => typeof c === "function" ? c() : c ); return mode === "all" ? evaluatedConditions.every((c) => c) : evaluatedConditions.some((c) => c); }, [mode, conditions]); return useConditionalPermission( combinedCondition, action, resource, context, defaultValue ); } // src/hooks/useEvaluate.ts var import_react12 = require("react"); function useEvaluate() { const { evaluate } = useACLContext(); return evaluate; } function useEvaluationResult(action, resource, context) { const { evaluate, user } = useACLContext(); return (0, import_react12.useMemo)(() => { return evaluate(action, resource, context); }, [evaluate, action, resource, context, user]); } function usePermissionAnalysis() { const { evaluate, user } = useACLContext(); const explainDenial = (0, import_react12.useCallback)( (action, resource, context) => { const result = evaluate(action, resource, context); if (result.allowed) { return "Permiss\xE3o concedida"; } return result.reason || "Permiss\xE3o negada por raz\xE3o desconhecida"; }, [evaluate] ); const findMatchingRule = (0, import_react12.useCallback)( (action, resource, context) => { const result = evaluate(action, resource, context); return result.matchedRule || null; }, [evaluate] ); const testPermission = (0, import_react12.useCallback)( (action, resource, contexts) => { if (!contexts || contexts.length === 0) { const result = evaluate(action, resource); return [{ context: void 0, ...result }]; } return contexts.map((context) => ({ context, ...evaluate(action, resource, context) })); }, [evaluate] ); return (0, import_react12.useMemo)( () => ({ explainDenial, findMatchingRule, testPermission }), [explainDenial, findMatchingRule, testPermission, user] ); } // src/hooks/useRoleHierarchy.ts var import_react13 = require("react"); function useRoleHierarchy() { const { engine, user } = useACLContext(); const { allRoles, directRoles, inheritedRoles, inheritanceMap } = (0, import_react13.useMemo)(() => { if (!user) { return { allRoles: [], directRoles: [], inheritedRoles: [], inheritanceMap: {} }; } const direct = user.roles; const all = new Set(direct); const inheritance = {}; const collectInheritedRoles = (roleName, visited = /* @__PURE__ */ new Set()) => { if (visited.has(roleName)) return; visited.add(roleName); const role = engine.getRole(roleName); if (!role) return; inheritance[roleName] = role.inherits || []; if (role.inherits) { role.inherits.forEach((inheritedRole) => { all.add(inheritedRole); collectInheritedRoles(inheritedRole, visited); }); } }; direct.forEach((roleName) => collectInheritedRoles(roleName)); const allArray = Array.from(all); const inherited = allArray.filter((role) => !direct.includes(role)); return { allRoles: allArray, directRoles: direct, inheritedRoles: inherited, inheritanceMap: inheritance }; }, [engine, user]); const hierarchyTree = (0, import_react13.useMemo)(() => { if (!user) return []; const buildNode = (roleName, direct) => { var _a; const role = engine.getRole(roleName); const children = ((_a = role == null ? void 0 : role.inherits) == null ? void 0 : _a.map((child) => buildNode(child, false))) || []; return { name: roleName, direct, children }; }; return directRoles.map((roleName) => buildNode(roleName, true)); }, [engine, user, directRoles]); const hasRole = (0, import_react13.useCallback)( (roleName) => { return allRoles.includes(roleName); }, [allRoles] ); const hasAnyRole = (0, import_react13.useCallback)( (roleNames) => { return roleNames.some((role) => allRoles.includes(role)); }, [allRoles] ); const hasAllRoles = (0, import_react13.useCallback)( (roleNames) => { return roleNames.every((role) => allRoles.includes(role)); }, [allRoles] ); const getRoleDetails = (0, import_react13.useCallback)( (roleName) => { return engine.getRole(roleName); }, [engine] ); return { allRoles, directRoles, inheritedRoles, inheritanceMap, hierarchyTree, hasRole, hasAnyRole, hasAllRoles, getRoleDetails }; } function useHasRole(roleNames, mode = "any") { const { hasRole, hasAnyRole, hasAllRoles } = useRoleHierarchy(); return (0, import_react13.useMemo)(() => { const roles = Array.isArray(roleNames) ? roleNames : [roleNames]; if (roles.length === 1) { return hasRole(roles[0]); } return mode === "all" ? hasAllRoles(roles) : hasAnyRole(roles); }, [roleNames, mode, hasRole, hasAnyRole, hasAllRoles]); } function useRoleInfo(roleName) { const { getRoleDetails } = useRoleHierarchy(); return (0, import_react13.useMemo)(() => { return getRoleDetails(roleName); }, [getRoleDetails, roleName]); } // src/hooks/usePermissionChange.ts var import_react14 = require("react"); function usePermissionChange(action, resource, callback) { const { can, user } = useACLContext(); const previousValueRef = (0, import_react14.useRef)(null); const previousUserRef = (0, import_react14.useRef)(user); (0, import_react14.useEffect)(() => { var _a; const currentValue = can(action, resource); if (previousValueRef.current === null) { previousValueRef.current = currentValue; previousUserRef.current = user; return; } const hasPermissionChanged = currentValue !== previousValueRef.current; const hasUserChanged = (user == null ? void 0 : user.id) !== ((_a = previousUserRef.current) == null ? void 0 : _a.id); if (hasPermissionChanged || hasUserChanged) { callback(currentValue, previousValueRef.current, { action, resource, user }); previousValueRef.current = currentValue; previousUserRef.current = user; } }, [can, user, action, resource, callback]); } function useMultiplePermissionChange(permissions, callback) { const { can, user } = useACLContext(); const previousValuesRef = (0, import_react14.useRef)(/* @__PURE__ */ new Map()); const previousUserRef = (0, import_react14.useRef)(user); (0, import_react14.useEffect)(() => { var _a; const changes = []; let hasAnyChange = false; const hasUserChanged = (user == null ? void 0 : user.id) !== ((_a = previousUserRef.current) == null ? void 0 : _a.id); permissions.forEach(({ action, resource }) => { const key = `${action}:${resource || "none"}`; const currentValue = can(action, resource); const previousValue = previousValuesRef.current.get(key); if (previousValue === void 0 || hasUserChanged) { previousValuesRef.current.set(key, currentValue); if (previousValue !== void 0) { hasAnyChange = true; changes.push({ action, resource, newValue: currentValue, previousValue }); } } else if (currentValue !== previousValue) { hasAnyChange = true; previousValuesRef.current.set(key, currentValue); changes.push({ action, resource, newValue: currentValue, previousValue }); } }); if (hasAnyChange) { callback(changes); } previousUserRef.current = user; }, [can, user, permissions, callback]); } function usePermissionEffect(action, resource, onGain, onLose) { usePermissionChange(action, resource, (newValue, previousValue) => { if (newValue && !previousValue && onGain) { onGain(); } else if (!newValue && previousValue && onLose) { onLose(); } }); } function usePermissionChangeDetector() { const { can } = useACLContext(); return (0, import_react14.useCallback)( (action, resource) => { const initialValue = can(action, resource); return { action, resource, previousValue: initialValue, get currentValue() { return can(action, resource); }, hasChanged() { return this.currentValue !== this.previousValue; } }; }, [can] ); } // src/components.tsx var import_react15 = require("react"); var import_jsx_runtime2 = require("react/jsx-runtime"); function Can({ action, resource, context, children, fallback = null, passThrough = false }) { const allowed = useCan(action, resource, context); if (allowed) { return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children }); } if (passThrough && (0, import_react15.isValidElement)(children)) { return (0, import_react15.cloneElement)(children, { disabled: true, "data-permission-denied": true, "aria-disabled": true }); } return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback }); } function Cannot({ action, resource, context, children, passThrough = false }) { const allowed = useCan(action, resource, context); if (allowed && !passThrough) { return null; } if (allowed && passThrough && (0, import_react15.isValidElement)(children)) { return (0, import_react15.cloneElement)(children, { disabled: false, "data-permission-granted": true, "aria-disabled": false }); } return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children }); } function IfCan({ action, resource, context, yes, no = null }) { const allowed = useCan(action, resource, context); return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: allowed ? yes : no }); } function CanAny({ permissions, children, fallback = null }) { const canAny = permissions.some( (perm) => useCan(perm.action, perm.resource, perm.context) ); return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: canAny ? children : fallback }); } function CanAll({ permissions, children, fallback = null }) { const canAll = permissions.every( (perm) => useCan(perm.action, perm.resource, perm.context) ); return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: canAll ? children : fallback }); } function withCan(Component, action, resource, options) { return function WithCanComponent(props) { var _a; const allowed = useCan(action, resource, props.context); if (!allowed) { (_a = options == null ? void 0 : options.onDenied) == null ? void 0 : _a.call(options); if (options == null ? void 0 : options.fallback) { const FallbackComponent = options.fallback; return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FallbackComponent, { ...props }); } if ((options == null ? void 0 : options.redirectTo) && typeof window !== "undefined") { window.location.href = options.redirectTo; } return null; } return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Component, { ...props }); }; } function PermissionClass({ action, resource, context, className, deniedClassName = "", children }) { const allowed = useCan(action, resource, context); if (!(0, import_react15.isValidElement)(children)) { return null; } const appliedClassName = allowed ? className : deniedClassName; const existingClassName = children.props.className || ""; return (0, import_react15.cloneElement)(children, { className: `${existingClassName} ${appliedClassName}`.trim(), "data-permission": allowed ? "granted" : "denied" }); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ACLProvider, Can, CanAll, CanAny, Cannot, IfCan, PermissionClass, useACL, useACLContext, useAvailableActions, useAvailableResources, useCan, useCanAll, useCanAllActions, useCanAny, useCanAnyAction, useCanArray, useCanMap, useCanMultiple, useCannot, useCombinedConditionalPermission, useConditionalChecker, useConditionalPermission, useConditionalPermissions, useEvaluate, useEvaluationResult, useHasRole, useMultiplePermissionChange, useMultiplePermissionStates, useMultipleResourceACL, usePermissionAnalysis, usePermissionBoolean, usePermissionChange, usePermissionChangeDetector, usePermissionEffect, usePermissionHelpers, usePermissionState, usePermissions, usePermissionsList, useResourceACL, useResourcePermissions, useRoleHierarchy, useRoleInfo, withCan }); //# sourceMappingURL=index.js.map