diff --git a/packages/permissions/README.md b/packages/permissions/README.md index 12093a873e..d059fe8dae 100644 --- a/packages/permissions/README.md +++ b/packages/permissions/README.md @@ -24,16 +24,31 @@ npm install @object-ui/permissions ## Quick Start ```tsx -import { PermissionProvider, usePermissions, PermissionGuard } from '@object-ui/permissions'; +import { + PermissionProvider, + PermissionGuard, + usePermissions, + type ObjectPermissionConfig, + type RoleDefinition, +} from '@object-ui/permissions'; + +const roles: RoleDefinition[] = [ + { name: 'admin', label: 'Administrator' }, + { name: 'editor', label: 'Editor' }, +]; + +const permissions: ObjectPermissionConfig[] = [ + { + object: 'orders', + roles: { + editor: { actions: ['read', 'create', 'update'] }, + }, + }, +]; function App() { return ( - + ); @@ -45,15 +60,19 @@ function Dashboard() { return (

Orders

- No access

}> + No access

}>
- {can('delete', 'orders') && } + {can('orders', 'delete') && }
); } ``` +Roles are `RoleDefinition` records (identity and inheritance); what a role may +do lives in each object's `ObjectPermissionConfig.roles` map, and `userRoles` +names the roles the current user actually holds. + ## API ### PermissionProvider @@ -61,39 +80,81 @@ function Dashboard() { Wraps your application with permission context: ```tsx - - - +import { + PermissionProvider, + type ObjectPermissionConfig, + type RoleDefinition, +} from '@object-ui/permissions'; + +declare const roleDefinitions: RoleDefinition[]; +declare const permissionMap: ObjectPermissionConfig[]; + +function App() { + return
Your application
; +} + +const tree = ( + + + +); ``` ### usePermissions -Hook for checking permissions programmatically: +Hook for checking permissions programmatically. `can` and `cannot` take the +object first and the action second: ```tsx -const { can, cannot, roles } = usePermissions(); +import { usePermissions } from '@object-ui/permissions'; -if (can('update', 'orders')) { - // allow editing +function OrderToolbar() { + const { can, cannot, roles } = usePermissions(); + + if (can('orders', 'update')) { + // allow editing + } + + return

{cannot('orders', 'delete') ? 'Read only' : roles.join(', ')}

; } ``` ### useFieldPermissions -Hook for field-level permission checks: +Hook for field-level permission checks. It takes the object name, and returns +predicates you call per field: ```tsx -const { isVisible, isEditable } = useFieldPermissions('orders', 'discount'); +import { useFieldPermissions } from '@object-ui/permissions'; + +function DiscountField() { + const { canRead, canWrite } = useFieldPermissions('orders'); + + const isVisible = canRead('discount'); + const isEditable = canWrite('discount'); + + return isVisible ? : null; +} ``` ### PermissionGuard -Conditionally renders children based on permissions: +Conditionally renders children based on permissions. `fallback` selects the +denied behaviour, and `fallbackContent` carries the node rendered for +`fallback="custom"`: ```tsx -Read only}> - - +import { PermissionGuard } from '@object-ui/permissions'; + +function DeleteButton() { + return ; +} + +const guard = ( + Read only}> + + +); ``` ### evaluatePermission @@ -101,14 +162,25 @@ Conditionally renders children based on permissions: Programmatic permission evaluation: ```tsx -import { evaluatePermission } from '@object-ui/permissions'; +import { + evaluatePermission, + type ObjectPermissionConfig, + type RoleDefinition, +} from '@object-ui/permissions'; + +declare const roleDefinitions: RoleDefinition[]; +declare const permissionConfig: ObjectPermissionConfig[]; const result = evaluatePermission({ - action: 'update', - resource: 'orders', - roles: ['editor'], + roles: roleDefinitions, permissions: permissionConfig, + userRoles: ['editor'], + user: { id: 'user-1', roles: ['editor'] }, + object: 'orders', + action: 'update', }); + +result.allowed; // true | false ``` ### createPermissionStore @@ -116,8 +188,22 @@ const result = evaluatePermission({ Creates a permission store for advanced use cases: ```tsx -const store = createPermissionStore(permissionConfig); -store.check('read', 'orders'); // true | false +import { + createPermissionStore, + type ObjectPermissionConfig, + type RoleDefinition, +} from '@object-ui/permissions'; + +declare const roleDefinitions: RoleDefinition[]; +declare const permissionConfig: ObjectPermissionConfig[]; + +const store = createPermissionStore({ + roles: roleDefinitions, + permissions: permissionConfig, + userRoles: ['editor'], +}); + +store.check('orders', 'read'); // PermissionCheckResult: { allowed, reason?, ... } ``` ## Links diff --git a/scripts/check-doc-snippet-types.mjs b/scripts/check-doc-snippet-types.mjs index 67d7a763de..30beb8aa58 100644 --- a/scripts/check-doc-snippet-types.mjs +++ b/scripts/check-doc-snippet-types.mjs @@ -719,8 +719,6 @@ const UNGATED_DOCS = { '2 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; 1 unresolved-module diagnostic(s)', 'packages/layout/README.md': '3 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; 3 unresolved-module diagnostic(s)', - 'packages/permissions/README.md': - '12 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2322x4 TS2345x1 TS2353x1 — candidate real defects, un-triaged', 'packages/plugin-ai/README.md': '5 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2322x3 — candidate real defects, un-triaged', 'packages/plugin-charts/README.md':