react-auth-gate 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +653 -0
  3. package/dist/core/ruleEngine.d.ts +47 -0
  4. package/dist/core/ruleEngine.d.ts.map +1 -0
  5. package/dist/core/ruleEngine.js +138 -0
  6. package/dist/core/types.d.ts +122 -0
  7. package/dist/core/types.d.ts.map +1 -0
  8. package/dist/core/types.js +7 -0
  9. package/dist/devtools/DevPanel.d.ts +13 -0
  10. package/dist/devtools/DevPanel.d.ts.map +1 -0
  11. package/dist/devtools/DevPanel.js +308 -0
  12. package/dist/devtools/DevStore.d.ts +80 -0
  13. package/dist/devtools/DevStore.d.ts.map +1 -0
  14. package/dist/devtools/DevStore.js +152 -0
  15. package/dist/devtools/PermissionsRoot.d.ts +33 -0
  16. package/dist/devtools/PermissionsRoot.d.ts.map +1 -0
  17. package/dist/devtools/PermissionsRoot.js +46 -0
  18. package/dist/devtools/useDevRegister.d.ts +17 -0
  19. package/dist/devtools/useDevRegister.d.ts.map +1 -0
  20. package/dist/devtools/useDevRegister.js +29 -0
  21. package/dist/index.d.ts +22 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +21 -0
  24. package/dist/react/Permissioned.d.ts +41 -0
  25. package/dist/react/Permissioned.d.ts.map +1 -0
  26. package/dist/react/Permissioned.js +29 -0
  27. package/dist/react/PermissionsGate.d.ts +79 -0
  28. package/dist/react/PermissionsGate.d.ts.map +1 -0
  29. package/dist/react/PermissionsGate.js +101 -0
  30. package/dist/react/PermissionsProvider.d.ts +39 -0
  31. package/dist/react/PermissionsProvider.d.ts.map +1 -0
  32. package/dist/react/PermissionsProvider.js +93 -0
  33. package/dist/react/ProtectedRoute.d.ts +80 -0
  34. package/dist/react/ProtectedRoute.d.ts.map +1 -0
  35. package/dist/react/ProtectedRoute.js +92 -0
  36. package/dist/react/usePermission.d.ts +50 -0
  37. package/dist/react/usePermission.d.ts.map +1 -0
  38. package/dist/react/usePermission.js +71 -0
  39. package/package.json +56 -0
@@ -0,0 +1,101 @@
1
+ /**
2
+ * PermissionsGate Component
3
+ *
4
+ * Declarative permission boundary for your components.
5
+ * Automatically hides or disables children based on permission checks.
6
+ */
7
+ import React, { cloneElement } from 'react';
8
+ import { usePermission } from './usePermission';
9
+ /**
10
+ * PermissionsGate Component
11
+ *
12
+ * Wraps components with permission-based access control.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * // Hide button if user can't edit
17
+ * <PermissionsGate allow="user.edit" resource={user}>
18
+ * <EditButton />
19
+ * </PermissionsGate>
20
+ *
21
+ * // Disable button instead of hiding
22
+ * <PermissionsGate allow="post.delete" resource={post} mode="disable">
23
+ * <DeleteButton />
24
+ * </PermissionsGate>
25
+ *
26
+ * // Check multiple permissions (any)
27
+ * <PermissionsGate any={["admin", "moderator"]}>
28
+ * <AdminPanel />
29
+ * </PermissionsGate>
30
+ *
31
+ * // Check multiple permissions (all)
32
+ * <PermissionsGate all={["post.edit", "post.publish"]}>
33
+ * <PublishButton />
34
+ * </PermissionsGate>
35
+ *
36
+ * // Show fallback when denied
37
+ * <PermissionsGate allow="premium.feature" fallback={<UpgradePrompt />}>
38
+ * <PremiumFeature />
39
+ * </PermissionsGate>
40
+ * ```
41
+ */
42
+ export function PermissionsGate({ allow, any, all, resource, fallback, mode = 'hide', children, }) {
43
+ // Determine the permission check and evaluation mode
44
+ let check;
45
+ let evalMode = 'any';
46
+ if (allow !== undefined) {
47
+ check = allow;
48
+ evalMode = 'any';
49
+ }
50
+ else if (any !== undefined) {
51
+ check = any;
52
+ evalMode = 'any';
53
+ }
54
+ else if (all !== undefined) {
55
+ check = all;
56
+ evalMode = 'all';
57
+ }
58
+ else {
59
+ // No permission check specified - deny by default
60
+ console.warn('PermissionsGate: No permission check specified (allow, any, or all). Denying access.');
61
+ return fallback ? React.createElement(React.Fragment, null, fallback) : null;
62
+ }
63
+ const { allowed, loading } = usePermission(check, resource, evalMode);
64
+ // While loading, optionally show loading state
65
+ // For now, we treat loading as "not allowed" for security
66
+ if (loading) {
67
+ return mode === 'hide' ? null : React.createElement(React.Fragment, null, children);
68
+ }
69
+ // Permission denied
70
+ if (!allowed) {
71
+ if (mode === 'hide') {
72
+ return fallback ? React.createElement(React.Fragment, null, fallback) : null;
73
+ }
74
+ // mode === 'disable'
75
+ // Try to add disabled prop to children
76
+ return React.createElement(React.Fragment, null, disableChildren(children));
77
+ }
78
+ // Permission granted
79
+ return React.createElement(React.Fragment, null, children);
80
+ }
81
+ /**
82
+ * Helper function to add disabled prop to React elements
83
+ */
84
+ function disableChildren(children) {
85
+ return React.Children.map(children, (child) => {
86
+ if (!React.isValidElement(child)) {
87
+ return child;
88
+ }
89
+ // Clone element with disabled prop
90
+ // This works for standard HTML elements and components that accept disabled prop
91
+ return cloneElement(child, {
92
+ disabled: true,
93
+ 'aria-disabled': true,
94
+ style: {
95
+ ...(child.props.style || {}),
96
+ opacity: 0.5,
97
+ cursor: 'not-allowed',
98
+ },
99
+ });
100
+ });
101
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * PermissionsProvider
3
+ *
4
+ * Root provider component that establishes permission context for the entire app.
5
+ * Manages user, roles, permissions, rules, and feature flags.
6
+ */
7
+ import React, { ReactNode } from 'react';
8
+ import type { PermissionsConfig, PermissionsContextValue, PermissionEvaluation } from '../core/types';
9
+ /**
10
+ * Hook to access the permissions context
11
+ * Throws if used outside of PermissionsProvider
12
+ */
13
+ export declare function usePermissionsContext<TUser = any>(): PermissionsContextValue<TUser>;
14
+ interface PermissionsProviderProps<TUser = any> extends PermissionsConfig<TUser> {
15
+ children: ReactNode;
16
+ /** Internal: Dev tools registration callback */
17
+ onEvaluationRegister?: (evaluation: PermissionEvaluation) => void;
18
+ }
19
+ /**
20
+ * PermissionsProvider Component
21
+ *
22
+ * Wrap your app with this provider to enable permission checking throughout.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <PermissionsProvider
27
+ * user={currentUser}
28
+ * roles={['admin', 'editor']}
29
+ * permissions={['post.edit', 'post.delete']}
30
+ * rules={customRules}
31
+ * flags={{ newUI: true }}
32
+ * >
33
+ * <App />
34
+ * </PermissionsProvider>
35
+ * ```
36
+ */
37
+ export declare function PermissionsProvider<TUser = any>({ user, roles, permissions, rules, flags, enableDevTools, children, onEvaluationRegister, }: PermissionsProviderProps<TUser>): React.JSX.Element;
38
+ export {};
39
+ //# sourceMappingURL=PermissionsProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionsProvider.d.ts","sourceRoot":"","sources":["../../src/react/PermissionsProvider.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,EAAmD,SAAS,EAAE,MAAM,OAAO,CAAC;AAC1F,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EAEvB,oBAAoB,EACrB,MAAM,eAAe,CAAC;AASvB;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,GAAG,GAAG,KAAK,uBAAuB,CAAC,KAAK,CAAC,CAUnF;AAED,UAAU,wBAAwB,CAAC,KAAK,GAAG,GAAG,CAAE,SAAQ,iBAAiB,CAAC,KAAK,CAAC;IAC9E,QAAQ,EAAE,SAAS,CAAC;IACpB,gDAAgD;IAChD,oBAAoB,CAAC,EAAE,CAAC,UAAU,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACnE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,GAAG,GAAG,EAAE,EAC/C,IAAI,EACJ,KAAU,EACV,WAAgB,EAChB,KAAU,EACV,KAAU,EACV,cAAc,EACd,QAAQ,EACR,oBAAoB,GACrB,EAAE,wBAAwB,CAAC,KAAK,CAAC,qBAiFjC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * PermissionsProvider
3
+ *
4
+ * Root provider component that establishes permission context for the entire app.
5
+ * Manages user, roles, permissions, rules, and feature flags.
6
+ */
7
+ import React, { createContext, useContext, useMemo, useCallback } from 'react';
8
+ import { evaluatePermission as evaluatePermissionCore, createPermissionContext, } from '../core/ruleEngine';
9
+ // Create the context
10
+ const PermissionsContext = createContext(null);
11
+ /**
12
+ * Hook to access the permissions context
13
+ * Throws if used outside of PermissionsProvider
14
+ */
15
+ export function usePermissionsContext() {
16
+ const context = useContext(PermissionsContext);
17
+ if (!context) {
18
+ throw new Error('usePermissionsContext must be used within a PermissionsProvider');
19
+ }
20
+ return context;
21
+ }
22
+ /**
23
+ * PermissionsProvider Component
24
+ *
25
+ * Wrap your app with this provider to enable permission checking throughout.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * <PermissionsProvider
30
+ * user={currentUser}
31
+ * roles={['admin', 'editor']}
32
+ * permissions={['post.edit', 'post.delete']}
33
+ * rules={customRules}
34
+ * flags={{ newUI: true }}
35
+ * >
36
+ * <App />
37
+ * </PermissionsProvider>
38
+ * ```
39
+ */
40
+ export function PermissionsProvider({ user, roles = [], permissions = [], rules = {}, flags = {}, enableDevTools, children, onEvaluationRegister, }) {
41
+ // Auto-enable dev tools in development unless explicitly disabled
42
+ const devToolsEnabled = useMemo(() => {
43
+ if (enableDevTools !== undefined) {
44
+ return enableDevTools;
45
+ }
46
+ // Check if we're in development mode
47
+ return process.env.NODE_ENV !== 'production';
48
+ }, [enableDevTools]);
49
+ /**
50
+ * Core permission evaluation function
51
+ * Used by all permission-checking components and hooks
52
+ */
53
+ const evaluatePermission = useCallback(async (check, resource, mode = 'any') => {
54
+ const context = createPermissionContext(user, resource, roles, permissions, flags);
55
+ const startTime = performance.now();
56
+ const result = await evaluatePermissionCore(check, context, rules, mode);
57
+ // Register with dev tools if enabled
58
+ if (devToolsEnabled && onEvaluationRegister) {
59
+ const evaluation = {
60
+ id: `eval-${Date.now()}-${Math.random()}`,
61
+ timestamp: Date.now(),
62
+ check: typeof check === 'function' ? 'inline' : check,
63
+ resource,
64
+ allowed: result.allowed,
65
+ ruleResults: result.ruleResults,
66
+ mode,
67
+ };
68
+ onEvaluationRegister(evaluation);
69
+ }
70
+ return result.allowed;
71
+ }, [user, roles, permissions, rules, flags, devToolsEnabled, onEvaluationRegister]);
72
+ // Memoize context value to prevent unnecessary re-renders
73
+ const contextValue = useMemo(() => ({
74
+ user,
75
+ roles,
76
+ permissions,
77
+ rules,
78
+ flags,
79
+ enableDevTools: devToolsEnabled,
80
+ evaluatePermission,
81
+ registerEvaluation: onEvaluationRegister,
82
+ }), [
83
+ user,
84
+ roles,
85
+ permissions,
86
+ rules,
87
+ flags,
88
+ devToolsEnabled,
89
+ evaluatePermission,
90
+ onEvaluationRegister,
91
+ ]);
92
+ return (React.createElement(PermissionsContext.Provider, { value: contextValue }, children));
93
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * ProtectedRoute Component
3
+ *
4
+ * Wrapper for route protection based on permissions.
5
+ * Works with any routing library (React Router, Next.js, etc.)
6
+ */
7
+ import React, { ReactNode } from 'react';
8
+ import type { PermissionCheck } from '../core/types';
9
+ export interface ProtectedRouteProps<TUser = any, TResource = any> {
10
+ /**
11
+ * Permission check required to access this route
12
+ */
13
+ allow: PermissionCheck<TUser, TResource>;
14
+ /**
15
+ * Resource to check permission against (optional)
16
+ */
17
+ resource?: TResource;
18
+ /**
19
+ * Content to render when permission is granted
20
+ */
21
+ children: ReactNode;
22
+ /**
23
+ * Content to render when permission is denied
24
+ * Typically a redirect or unauthorized message
25
+ */
26
+ fallback?: ReactNode;
27
+ /**
28
+ * Optional callback when access is denied
29
+ * Useful for analytics, logging, or custom redirects
30
+ */
31
+ onAccessDenied?: () => void;
32
+ }
33
+ /**
34
+ * ProtectedRoute Component
35
+ *
36
+ * Protects routes based on permissions.
37
+ * Framework-agnostic - works with any routing solution.
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * // With React Router
42
+ * <Route
43
+ * path="/admin"
44
+ * element={
45
+ * <ProtectedRoute
46
+ * allow="admin"
47
+ * fallback={<Navigate to="/login" />}
48
+ * >
49
+ * <AdminDashboard />
50
+ * </ProtectedRoute>
51
+ * }
52
+ * />
53
+ *
54
+ * // With Next.js
55
+ * function AdminPage() {
56
+ * const router = useRouter();
57
+ *
58
+ * return (
59
+ * <ProtectedRoute
60
+ * allow="admin"
61
+ * onAccessDenied={() => router.push('/login')}
62
+ * fallback={<div>Redirecting...</div>}
63
+ * >
64
+ * <AdminPanel />
65
+ * </ProtectedRoute>
66
+ * );
67
+ * }
68
+ *
69
+ * // Resource-based protection
70
+ * <ProtectedRoute
71
+ * allow="post.edit"
72
+ * resource={post}
73
+ * fallback={<Unauthorized />}
74
+ * >
75
+ * <EditPost post={post} />
76
+ * </ProtectedRoute>
77
+ * ```
78
+ */
79
+ export declare function ProtectedRoute<TUser = any, TResource = any>({ allow, resource, children, fallback, onAccessDenied, }: ProtectedRouteProps<TUser, TResource>): React.JSX.Element;
80
+ //# sourceMappingURL=ProtectedRoute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProtectedRoute.d.ts","sourceRoot":"","sources":["../../src/react/ProtectedRoute.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,WAAW,mBAAmB,CAAC,KAAK,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG;IAC/D;;OAEG;IACH,KAAK,EAAE,eAAe,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAEzC;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,cAAc,CAAC,KAAK,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG,EAAE,EAC3D,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,QAAkC,EAClC,cAAc,GACf,EAAE,mBAAmB,CAAC,KAAK,EAAE,SAAS,CAAC,qBAsBvC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * ProtectedRoute Component
3
+ *
4
+ * Wrapper for route protection based on permissions.
5
+ * Works with any routing library (React Router, Next.js, etc.)
6
+ */
7
+ import React from 'react';
8
+ import { usePermission } from './usePermission';
9
+ /**
10
+ * ProtectedRoute Component
11
+ *
12
+ * Protects routes based on permissions.
13
+ * Framework-agnostic - works with any routing solution.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * // With React Router
18
+ * <Route
19
+ * path="/admin"
20
+ * element={
21
+ * <ProtectedRoute
22
+ * allow="admin"
23
+ * fallback={<Navigate to="/login" />}
24
+ * >
25
+ * <AdminDashboard />
26
+ * </ProtectedRoute>
27
+ * }
28
+ * />
29
+ *
30
+ * // With Next.js
31
+ * function AdminPage() {
32
+ * const router = useRouter();
33
+ *
34
+ * return (
35
+ * <ProtectedRoute
36
+ * allow="admin"
37
+ * onAccessDenied={() => router.push('/login')}
38
+ * fallback={<div>Redirecting...</div>}
39
+ * >
40
+ * <AdminPanel />
41
+ * </ProtectedRoute>
42
+ * );
43
+ * }
44
+ *
45
+ * // Resource-based protection
46
+ * <ProtectedRoute
47
+ * allow="post.edit"
48
+ * resource={post}
49
+ * fallback={<Unauthorized />}
50
+ * >
51
+ * <EditPost post={post} />
52
+ * </ProtectedRoute>
53
+ * ```
54
+ */
55
+ export function ProtectedRoute({ allow, resource, children, fallback = React.createElement(DefaultUnauthorized, null), onAccessDenied, }) {
56
+ const { allowed, loading } = usePermission(allow, resource);
57
+ // Call onAccessDenied when permission is denied (only once)
58
+ React.useEffect(() => {
59
+ if (!loading && !allowed && onAccessDenied) {
60
+ onAccessDenied();
61
+ }
62
+ }, [loading, allowed, onAccessDenied]);
63
+ // While loading, show nothing or a loading indicator
64
+ if (loading) {
65
+ return React.createElement(DefaultLoading, null);
66
+ }
67
+ // Permission denied
68
+ if (!allowed) {
69
+ return React.createElement(React.Fragment, null, fallback);
70
+ }
71
+ // Permission granted
72
+ return React.createElement(React.Fragment, null, children);
73
+ }
74
+ /**
75
+ * Default loading component
76
+ */
77
+ function DefaultLoading() {
78
+ return (React.createElement("div", { style: { padding: '20px', textAlign: 'center' } }, "Loading..."));
79
+ }
80
+ /**
81
+ * Default unauthorized component
82
+ */
83
+ function DefaultUnauthorized() {
84
+ return (React.createElement("div", { style: {
85
+ padding: '40px',
86
+ textAlign: 'center',
87
+ maxWidth: '600px',
88
+ margin: '0 auto',
89
+ } },
90
+ React.createElement("h1", null, "Access Denied"),
91
+ React.createElement("p", null, "You do not have permission to access this resource.")));
92
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * usePermission Hook
3
+ *
4
+ * Check permissions programmatically in your components.
5
+ * Supports async rules and automatically re-evaluates when dependencies change.
6
+ */
7
+ import type { PermissionCheck } from '../core/types';
8
+ /**
9
+ * Hook to check if a permission is allowed
10
+ *
11
+ * @param check - Permission check (string, array, or function)
12
+ * @param resource - Optional resource to check against
13
+ * @param mode - Evaluation mode for arrays: 'any' (OR) or 'all' (AND)
14
+ * @returns Object with loading state and permission result
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * function EditButton({ user }) {
19
+ * const { allowed, loading } = usePermission('user.edit', user);
20
+ *
21
+ * if (loading) return <Spinner />;
22
+ *
23
+ * return (
24
+ * <button disabled={!allowed}>
25
+ * Edit User
26
+ * </button>
27
+ * );
28
+ * }
29
+ * ```
30
+ */
31
+ export declare function usePermission<TUser = any, TResource = any>(check: PermissionCheck<TUser, TResource>, resource?: TResource, mode?: 'any' | 'all'): {
32
+ allowed: boolean;
33
+ loading: boolean;
34
+ };
35
+ /**
36
+ * Simpler hook that only returns the boolean result
37
+ * Use when you don't need loading state
38
+ *
39
+ * @param check - Permission check
40
+ * @param resource - Optional resource
41
+ * @param mode - Evaluation mode
42
+ * @returns Boolean indicating if permission is allowed
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * const canEdit = usePermissionValue('user.edit', user);
47
+ * ```
48
+ */
49
+ export declare function usePermissionValue<TUser = any, TResource = any>(check: PermissionCheck<TUser, TResource>, resource?: TResource, mode?: 'any' | 'all'): boolean;
50
+ //# sourceMappingURL=usePermission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePermission.d.ts","sourceRoot":"","sources":["../../src/react/usePermission.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG,EACxD,KAAK,EAAE,eAAe,CAAC,KAAK,EAAE,SAAS,CAAC,EACxC,QAAQ,CAAC,EAAE,SAAS,EACpB,IAAI,GAAE,KAAK,GAAG,KAAa,GAC1B;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB,CA0BA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG,EAC7D,KAAK,EAAE,eAAe,CAAC,KAAK,EAAE,SAAS,CAAC,EACxC,QAAQ,CAAC,EAAE,SAAS,EACpB,IAAI,GAAE,KAAK,GAAG,KAAa,GAC1B,OAAO,CAGT"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * usePermission Hook
3
+ *
4
+ * Check permissions programmatically in your components.
5
+ * Supports async rules and automatically re-evaluates when dependencies change.
6
+ */
7
+ import { useEffect, useState } from 'react';
8
+ import { usePermissionsContext } from './PermissionsProvider';
9
+ /**
10
+ * Hook to check if a permission is allowed
11
+ *
12
+ * @param check - Permission check (string, array, or function)
13
+ * @param resource - Optional resource to check against
14
+ * @param mode - Evaluation mode for arrays: 'any' (OR) or 'all' (AND)
15
+ * @returns Object with loading state and permission result
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * function EditButton({ user }) {
20
+ * const { allowed, loading } = usePermission('user.edit', user);
21
+ *
22
+ * if (loading) return <Spinner />;
23
+ *
24
+ * return (
25
+ * <button disabled={!allowed}>
26
+ * Edit User
27
+ * </button>
28
+ * );
29
+ * }
30
+ * ```
31
+ */
32
+ export function usePermission(check, resource, mode = 'any') {
33
+ const context = usePermissionsContext();
34
+ const [state, setState] = useState({
35
+ allowed: false,
36
+ loading: true,
37
+ });
38
+ useEffect(() => {
39
+ let cancelled = false;
40
+ // Start evaluation
41
+ setState({ allowed: false, loading: true });
42
+ context.evaluatePermission(check, resource, mode).then((allowed) => {
43
+ if (!cancelled) {
44
+ setState({ allowed, loading: false });
45
+ }
46
+ });
47
+ // Cleanup function to prevent state updates after unmount
48
+ return () => {
49
+ cancelled = true;
50
+ };
51
+ }, [context.evaluatePermission, context.roles, context.permissions, context.flags, check, resource, mode]);
52
+ return state;
53
+ }
54
+ /**
55
+ * Simpler hook that only returns the boolean result
56
+ * Use when you don't need loading state
57
+ *
58
+ * @param check - Permission check
59
+ * @param resource - Optional resource
60
+ * @param mode - Evaluation mode
61
+ * @returns Boolean indicating if permission is allowed
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * const canEdit = usePermissionValue('user.edit', user);
66
+ * ```
67
+ */
68
+ export function usePermissionValue(check, resource, mode = 'any') {
69
+ const { allowed } = usePermission(check, resource, mode);
70
+ return allowed;
71
+ }
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "react-auth-gate",
3
+ "version": "0.0.1",
4
+ "description": "A production-grade React authorization framework for RBAC, PBAC, ABAC, feature flags, and async permission checks",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "keywords": [
19
+ "react",
20
+ "permissions",
21
+ "authorization",
22
+ "rbac",
23
+ "pbac",
24
+ "abac",
25
+ "feature-flags",
26
+ "access-control"
27
+ ],
28
+ "author": "Klejdi 2K",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/klejdi94/react-auth-gate"
33
+ },
34
+ "peerDependencies": {
35
+ "react": ">=16.8.0",
36
+ "react-dom": ">=16.8.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/jest": "^29.5.0",
40
+ "@types/react": "^18.2.0",
41
+ "@types/react-dom": "^18.2.0",
42
+ "jest": "^29.5.0",
43
+ "jest-environment-jsdom": "^29.5.0",
44
+ "ts-jest": "^29.1.0",
45
+ "typescript": "^5.3.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc",
49
+ "dev": "tsc --watch",
50
+ "test": "jest",
51
+ "test:watch": "jest --watch",
52
+ "test:coverage": "jest --coverage",
53
+ "prepublishOnly": "npm run build"
54
+ },
55
+ "sideEffects": false
56
+ }