secure-role-guard 1.0.0

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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +813 -0
  3. package/dist/adapters/express.d.mts +109 -0
  4. package/dist/adapters/express.d.ts +109 -0
  5. package/dist/adapters/express.js +122 -0
  6. package/dist/adapters/express.js.map +1 -0
  7. package/dist/adapters/express.mjs +118 -0
  8. package/dist/adapters/express.mjs.map +1 -0
  9. package/dist/adapters/index.d.mts +3 -0
  10. package/dist/adapters/index.d.ts +3 -0
  11. package/dist/adapters/index.js +181 -0
  12. package/dist/adapters/index.js.map +1 -0
  13. package/dist/adapters/index.mjs +171 -0
  14. package/dist/adapters/index.mjs.map +1 -0
  15. package/dist/adapters/nextjs.d.mts +140 -0
  16. package/dist/adapters/nextjs.d.ts +140 -0
  17. package/dist/adapters/nextjs.js +138 -0
  18. package/dist/adapters/nextjs.js.map +1 -0
  19. package/dist/adapters/nextjs.mjs +131 -0
  20. package/dist/adapters/nextjs.mjs.map +1 -0
  21. package/dist/core/index.d.mts +100 -0
  22. package/dist/core/index.d.ts +100 -0
  23. package/dist/core/index.js +132 -0
  24. package/dist/core/index.js.map +1 -0
  25. package/dist/core/index.mjs +125 -0
  26. package/dist/core/index.mjs.map +1 -0
  27. package/dist/index.d.mts +4 -0
  28. package/dist/index.d.ts +4 -0
  29. package/dist/index.js +238 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/index.mjs +222 -0
  32. package/dist/index.mjs.map +1 -0
  33. package/dist/react/index.d.mts +237 -0
  34. package/dist/react/index.d.ts +237 -0
  35. package/dist/react/index.js +177 -0
  36. package/dist/react/index.js.map +1 -0
  37. package/dist/react/index.mjs +167 -0
  38. package/dist/react/index.mjs.map +1 -0
  39. package/dist/types-CSUpaGsY.d.mts +76 -0
  40. package/dist/types-CSUpaGsY.d.ts +76 -0
  41. package/package.json +99 -0
@@ -0,0 +1,237 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { U as UserContext, a as RoleRegistry } from '../types-CSUpaGsY.js';
4
+
5
+ /**
6
+ * Shape of the permission context value.
7
+ */
8
+ type PermissionContextValue = {
9
+ /** Current user context */
10
+ readonly user: UserContext | null;
11
+ /** Role registry for permission lookups */
12
+ readonly registry: RoleRegistry;
13
+ /** Check if user has a specific permission */
14
+ readonly can: (permission: string) => boolean;
15
+ /** Check if user has ALL permissions */
16
+ readonly canAll: (permissions: readonly string[]) => boolean;
17
+ /** Check if user has ANY permission */
18
+ readonly canAny: (permissions: readonly string[]) => boolean;
19
+ };
20
+ /**
21
+ * React context for permissions.
22
+ * Default value denies all permissions (deny by default).
23
+ */
24
+ declare const PermissionContext: react.Context<PermissionContextValue>;
25
+
26
+ /**
27
+ * Secure Role Guard - React Provider
28
+ *
29
+ * Main provider component for permission management.
30
+ * Framework-agnostic: works with Next.js, Remix, Gatsby, CRA, Vite.
31
+ */
32
+
33
+ /**
34
+ * Props for the PermissionProvider component.
35
+ */
36
+ type PermissionProviderProps = {
37
+ /** Current user context (from your auth system) */
38
+ readonly user: UserContext | null;
39
+ /** Role registry created by defineRoles() */
40
+ readonly registry: RoleRegistry;
41
+ /** Child components */
42
+ readonly children: ReactNode;
43
+ };
44
+ /**
45
+ * Provides permission context to all child components.
46
+ *
47
+ * SECURITY: If user is null/undefined, all permission checks will deny by default.
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * import { defineRoles, PermissionProvider } from 'secure-role-guard';
52
+ *
53
+ * const roleRegistry = defineRoles({
54
+ * admin: ['user.read', 'user.update'],
55
+ * viewer: ['user.read']
56
+ * });
57
+ *
58
+ * function App() {
59
+ * const user = { roles: ['admin'] }; // From your auth system
60
+ *
61
+ * return (
62
+ * <PermissionProvider user={user} registry={roleRegistry}>
63
+ * <YourApp />
64
+ * </PermissionProvider>
65
+ * );
66
+ * }
67
+ * ```
68
+ */
69
+ declare function PermissionProvider({ user, registry, children, }: PermissionProviderProps): ReactNode;
70
+
71
+ /**
72
+ * Hook to access the full permission context.
73
+ *
74
+ * @returns Full PermissionContextValue
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * function UserProfile() {
79
+ * const { user, can, canAll } = usePermissions();
80
+ *
81
+ * if (can('user.read')) {
82
+ * return <UserDetails />;
83
+ * }
84
+ * return <AccessDenied />;
85
+ * }
86
+ * ```
87
+ */
88
+ declare function usePermissions(): PermissionContextValue;
89
+ /**
90
+ * Hook to check a single permission.
91
+ *
92
+ * @param permission - The permission to check
93
+ * @returns True if user has the permission
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * function EditButton() {
98
+ * const canEdit = useCan('user.update');
99
+ *
100
+ * if (!canEdit) return null;
101
+ * return <button>Edit User</button>;
102
+ * }
103
+ * ```
104
+ */
105
+ declare function useCan(permission: string): boolean;
106
+ /**
107
+ * Hook to check if user has ALL specified permissions.
108
+ *
109
+ * @param permissions - Array of permissions to check
110
+ * @returns True if user has all permissions
111
+ *
112
+ * @example
113
+ * ```tsx
114
+ * function AdminPanel() {
115
+ * const canManage = useCanAll(['user.read', 'user.update', 'user.delete']);
116
+ *
117
+ * if (!canManage) return <AccessDenied />;
118
+ * return <AdminDashboard />;
119
+ * }
120
+ * ```
121
+ */
122
+ declare function useCanAll(permissions: readonly string[]): boolean;
123
+ /**
124
+ * Hook to check if user has ANY of the specified permissions.
125
+ *
126
+ * @param permissions - Array of permissions to check
127
+ * @returns True if user has at least one permission
128
+ *
129
+ * @example
130
+ * ```tsx
131
+ * function ViewReports() {
132
+ * const canView = useCanAny(['report.admin', 'report.viewer']);
133
+ *
134
+ * if (!canView) return null;
135
+ * return <ReportList />;
136
+ * }
137
+ * ```
138
+ */
139
+ declare function useCanAny(permissions: readonly string[]): boolean;
140
+ /**
141
+ * Hook to get current user context.
142
+ *
143
+ * @returns Current UserContext or null
144
+ *
145
+ * @example
146
+ * ```tsx
147
+ * function UserInfo() {
148
+ * const user = useUser();
149
+ * return <span>User ID: {user?.userId ?? 'Anonymous'}</span>;
150
+ * }
151
+ * ```
152
+ */
153
+ declare function useUser(): UserContext | null;
154
+
155
+ /**
156
+ * Secure Role Guard - React Components
157
+ *
158
+ * Declarative components for conditional rendering based on permissions.
159
+ * Framework-agnostic: works with any React framework.
160
+ */
161
+
162
+ /**
163
+ * Props for the Can component.
164
+ */
165
+ type CanProps = {
166
+ /** Single permission to check */
167
+ readonly permission?: string;
168
+ /** Multiple permissions - user must have ALL (default behavior) */
169
+ readonly permissions?: readonly string[];
170
+ /** If true, user needs only ANY of the permissions */
171
+ readonly anyOf?: boolean;
172
+ /** Content to render if permission granted */
173
+ readonly children: ReactNode;
174
+ /** Optional fallback content if permission denied */
175
+ readonly fallback?: ReactNode;
176
+ };
177
+ /**
178
+ * Conditionally renders children based on user permissions.
179
+ *
180
+ * SECURITY: If no permission matches, nothing is rendered (deny by default).
181
+ *
182
+ * @example Single permission
183
+ * ```tsx
184
+ * <Can permission="user.update">
185
+ * <EditButton />
186
+ * </Can>
187
+ * ```
188
+ *
189
+ * @example Multiple permissions (ALL required)
190
+ * ```tsx
191
+ * <Can permissions={['user.read', 'user.update']}>
192
+ * <UserEditor />
193
+ * </Can>
194
+ * ```
195
+ *
196
+ * @example Multiple permissions (ANY required)
197
+ * ```tsx
198
+ * <Can permissions={['admin', 'moderator']} anyOf>
199
+ * <ModPanel />
200
+ * </Can>
201
+ * ```
202
+ *
203
+ * @example With fallback
204
+ * ```tsx
205
+ * <Can permission="premium.access" fallback={<UpgradePrompt />}>
206
+ * <PremiumContent />
207
+ * </Can>
208
+ * ```
209
+ */
210
+ declare function Can({ permission, permissions, anyOf, children, fallback, }: CanProps): ReactNode;
211
+ /**
212
+ * Props for the Cannot component.
213
+ */
214
+ type CannotProps = {
215
+ /** Single permission to check */
216
+ readonly permission?: string;
217
+ /** Multiple permissions to check */
218
+ readonly permissions?: readonly string[];
219
+ /** If true, blocked when user has ANY permission */
220
+ readonly anyOf?: boolean;
221
+ /** Content to render if permission is NOT granted */
222
+ readonly children: ReactNode;
223
+ };
224
+ /**
225
+ * Inverse of Can - renders children when user does NOT have the permission.
226
+ * Useful for showing upgrade prompts, locked features, etc.
227
+ *
228
+ * @example
229
+ * ```tsx
230
+ * <Cannot permission="premium.access">
231
+ * <UpgradePrompt />
232
+ * </Cannot>
233
+ * ```
234
+ */
235
+ declare function Cannot({ permission, permissions, anyOf, children, }: CannotProps): ReactNode;
236
+
237
+ export { Can, type CanProps, Cannot, type CannotProps, PermissionContext, type PermissionContextValue, PermissionProvider, type PermissionProviderProps, useCan, useCanAll, useCanAny, usePermissions, useUser };
@@ -0,0 +1,177 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/react/context.tsx
7
+ var defaultContextValue = {
8
+ user: null,
9
+ registry: {
10
+ getPermissions: () => Object.freeze([]),
11
+ hasRole: () => false,
12
+ getRoleNames: () => Object.freeze([])
13
+ },
14
+ can: () => false,
15
+ canAll: () => false,
16
+ canAny: () => false
17
+ };
18
+ var PermissionContext = react.createContext(defaultContextValue);
19
+ PermissionContext.displayName = "PermissionContext";
20
+
21
+ // src/core/permission-engine.ts
22
+ var WILDCARD_PERMISSION = "*";
23
+ function collectUserPermissions(user, registry) {
24
+ const permissions = /* @__PURE__ */ new Set();
25
+ if (user.permissions !== void 0) {
26
+ for (const permission of user.permissions) {
27
+ permissions.add(permission);
28
+ }
29
+ }
30
+ if (user.roles !== void 0) {
31
+ for (const role of user.roles) {
32
+ const rolePermissions = registry.getPermissions(role);
33
+ for (const permission of rolePermissions) {
34
+ permissions.add(permission);
35
+ }
36
+ }
37
+ }
38
+ return permissions;
39
+ }
40
+ function hasWildcardAccess(permissions, requested) {
41
+ if (permissions.has(WILDCARD_PERMISSION)) {
42
+ return true;
43
+ }
44
+ const parts = requested.split(".");
45
+ let namespace = "";
46
+ for (let i = 0; i < parts.length - 1; i++) {
47
+ const part = parts[i];
48
+ if (part !== void 0) {
49
+ namespace = namespace === "" ? part : `${namespace}.${part}`;
50
+ if (permissions.has(`${namespace}.*`)) {
51
+ return true;
52
+ }
53
+ }
54
+ }
55
+ return false;
56
+ }
57
+ function canUser(user, permission, registry) {
58
+ if (user === null || user === void 0) {
59
+ return false;
60
+ }
61
+ if (permission === "" || permission.trim() === "") {
62
+ return false;
63
+ }
64
+ const userPermissions = collectUserPermissions(user, registry);
65
+ if (userPermissions.has(permission)) {
66
+ return true;
67
+ }
68
+ if (hasWildcardAccess(userPermissions, permission)) {
69
+ return true;
70
+ }
71
+ return false;
72
+ }
73
+ function canUserAll(user, permissions, registry) {
74
+ if (permissions.length === 0) {
75
+ return false;
76
+ }
77
+ for (const permission of permissions) {
78
+ if (!canUser(user, permission, registry)) {
79
+ return false;
80
+ }
81
+ }
82
+ return true;
83
+ }
84
+ function canUserAny(user, permissions, registry) {
85
+ if (permissions.length === 0) {
86
+ return false;
87
+ }
88
+ for (const permission of permissions) {
89
+ if (canUser(user, permission, registry)) {
90
+ return true;
91
+ }
92
+ }
93
+ return false;
94
+ }
95
+ function PermissionProvider({
96
+ user,
97
+ registry,
98
+ children
99
+ }) {
100
+ const contextValue = react.useMemo(
101
+ () => ({
102
+ user,
103
+ registry,
104
+ can: (permission) => canUser(user, permission, registry),
105
+ canAll: (permissions) => canUserAll(user, permissions, registry),
106
+ canAny: (permissions) => canUserAny(user, permissions, registry)
107
+ }),
108
+ [user, registry]
109
+ );
110
+ return /* @__PURE__ */ jsxRuntime.jsx(PermissionContext.Provider, { value: contextValue, children });
111
+ }
112
+ function usePermissions() {
113
+ return react.useContext(PermissionContext);
114
+ }
115
+ function useCan(permission) {
116
+ const { can } = react.useContext(PermissionContext);
117
+ return can(permission);
118
+ }
119
+ function useCanAll(permissions) {
120
+ const { canAll } = react.useContext(PermissionContext);
121
+ return canAll(permissions);
122
+ }
123
+ function useCanAny(permissions) {
124
+ const { canAny } = react.useContext(PermissionContext);
125
+ return canAny(permissions);
126
+ }
127
+ function useUser() {
128
+ const { user } = react.useContext(PermissionContext);
129
+ return user;
130
+ }
131
+
132
+ // src/react/components.tsx
133
+ function Can({
134
+ permission,
135
+ permissions,
136
+ anyOf = false,
137
+ children,
138
+ fallback = null
139
+ }) {
140
+ if (permission !== void 0) {
141
+ const allowed = useCan(permission);
142
+ return allowed ? children : fallback;
143
+ }
144
+ if (permissions !== void 0 && permissions.length > 0) {
145
+ const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);
146
+ return allowed ? children : fallback;
147
+ }
148
+ return fallback;
149
+ }
150
+ function Cannot({
151
+ permission,
152
+ permissions,
153
+ anyOf = false,
154
+ children
155
+ }) {
156
+ if (permission !== void 0) {
157
+ const allowed = useCan(permission);
158
+ return allowed ? null : children;
159
+ }
160
+ if (permissions !== void 0 && permissions.length > 0) {
161
+ const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);
162
+ return allowed ? null : children;
163
+ }
164
+ return children;
165
+ }
166
+
167
+ exports.Can = Can;
168
+ exports.Cannot = Cannot;
169
+ exports.PermissionContext = PermissionContext;
170
+ exports.PermissionProvider = PermissionProvider;
171
+ exports.useCan = useCan;
172
+ exports.useCanAll = useCanAll;
173
+ exports.useCanAny = useCanAny;
174
+ exports.usePermissions = usePermissions;
175
+ exports.useUser = useUser;
176
+ //# sourceMappingURL=index.js.map
177
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/context.tsx","../../src/core/permission-engine.ts","../../src/react/provider.tsx","../../src/react/hooks.ts","../../src/react/components.tsx"],"names":["createContext","useMemo","useContext"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Secure Role Guard - React Context\r\n *\r\n * Framework-agnostic React context for permission management.\r\n * Works with Next.js, Remix, Gatsby, CRA, Vite, and any React framework.\r\n */\r\n\r\nimport { createContext } from \"react\";\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\n\r\n/**\r\n * Shape of the permission context value.\r\n */\r\nexport type PermissionContextValue = {\r\n /** Current user context */\r\n readonly user: UserContext | null;\r\n /** Role registry for permission lookups */\r\n readonly registry: RoleRegistry;\r\n /** Check if user has a specific permission */\r\n readonly can: (permission: string) => boolean;\r\n /** Check if user has ALL permissions */\r\n readonly canAll: (permissions: readonly string[]) => boolean;\r\n /** Check if user has ANY permission */\r\n readonly canAny: (permissions: readonly string[]) => boolean;\r\n};\r\n\r\n/**\r\n * Default context value (deny all).\r\n * Used when no provider is present.\r\n */\r\nconst defaultContextValue: PermissionContextValue = {\r\n user: null,\r\n registry: {\r\n getPermissions: () => Object.freeze([]),\r\n hasRole: () => false,\r\n getRoleNames: () => Object.freeze([]),\r\n },\r\n can: () => false,\r\n canAll: () => false,\r\n canAny: () => false,\r\n};\r\n\r\n/**\r\n * React context for permissions.\r\n * Default value denies all permissions (deny by default).\r\n */\r\nexport const PermissionContext =\r\n createContext<PermissionContextValue>(defaultContextValue);\r\n\r\n// Set display name for React DevTools\r\nPermissionContext.displayName = \"PermissionContext\";\r\n","/**\r\n * Secure Role Guard - Permission Engine\r\n *\r\n * Pure functions for permission checking.\r\n * Zero side effects, zero dependencies, zero mutations.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\r\n\r\n/** Wildcard permission that grants all access */\r\nconst WILDCARD_PERMISSION = \"*\";\r\n\r\n/**\r\n * Collects all permissions for a user from their roles and direct permissions.\r\n *\r\n * @param user - The user context\r\n * @param registry - The role registry to resolve role permissions\r\n * @returns Set of all permissions (for efficient lookup)\r\n */\r\nfunction collectUserPermissions(\r\n user: UserContext,\r\n registry: RoleRegistry\r\n): ReadonlySet<string> {\r\n const permissions = new Set<string>();\r\n\r\n // Add direct user permissions\r\n if (user.permissions !== undefined) {\r\n for (const permission of user.permissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n\r\n // Add permissions from all user roles\r\n if (user.roles !== undefined) {\r\n for (const role of user.roles) {\r\n const rolePermissions = registry.getPermissions(role);\r\n for (const permission of rolePermissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n }\r\n\r\n return permissions;\r\n}\r\n\r\n/**\r\n * Checks if a permission set contains a wildcard that grants the requested permission.\r\n *\r\n * Supports:\r\n * - Exact wildcard (*) - grants everything\r\n * - Namespace wildcards (user.*) - grants all under namespace\r\n *\r\n * @param permissions - Set of permissions to check\r\n * @param requested - The permission being requested\r\n * @returns True if wildcard grants access\r\n */\r\nfunction hasWildcardAccess(\r\n permissions: ReadonlySet<string>,\r\n requested: string\r\n): boolean {\r\n // Check for global wildcard\r\n if (permissions.has(WILDCARD_PERMISSION)) {\r\n return true;\r\n }\r\n\r\n // Check for namespace wildcards (e.g., user.* grants user.read)\r\n const parts = requested.split(\".\");\r\n let namespace = \"\";\r\n\r\n for (let i = 0; i < parts.length - 1; i++) {\r\n const part = parts[i];\r\n if (part !== undefined) {\r\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\r\n if (permissions.has(`${namespace}.*`)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has a specific permission.\r\n *\r\n * SECURITY GUARANTEES:\r\n * - Deny by default: undefined/null/empty always returns false\r\n * - Pure function: no side effects, no mutations\r\n * - Deterministic: same inputs always produce same output\r\n *\r\n * @param user - The user context to check\r\n * @param permission - The permission required\r\n * @param registry - The role registry for resolving roles\r\n * @returns True if user has the permission, false otherwise\r\n *\r\n * @example\r\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\r\n */\r\nexport function canUser(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: No user context means no access\r\n if (user === null || user === undefined) {\r\n return false;\r\n }\r\n\r\n // DENY BY DEFAULT: Empty permission request is invalid\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return false;\r\n }\r\n\r\n const userPermissions = collectUserPermissions(user, registry);\r\n\r\n // Check for exact permission match\r\n if (userPermissions.has(permission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard access\r\n if (hasWildcardAccess(userPermissions, permission)) {\r\n return true;\r\n }\r\n\r\n // DENY BY DEFAULT\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions required\r\n * @param registry - The role registry\r\n * @returns True if user has ALL permissions\r\n *\r\n * @example\r\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\r\n */\r\nexport function canUserAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (!canUser(user, permission, registry)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions to check\r\n * @param registry - The role registry\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\r\n */\r\nexport function canUserAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (canUser(user, permission, registry)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Extended permission check with detailed result.\r\n * Useful for debugging and logging.\r\n *\r\n * @param user - The user context\r\n * @param permission - Permission to check\r\n * @param registry - The role registry\r\n * @returns PermissionCheckResult with allowed status and reason\r\n */\r\nexport function checkPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionCheckResult {\r\n if (user === null || user === undefined) {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"No user context provided\",\r\n });\r\n }\r\n\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"Empty permission requested\",\r\n });\r\n }\r\n\r\n const allowed = canUser(user, permission, registry);\r\n\r\n return Object.freeze({\r\n allowed,\r\n reason: allowed\r\n ? `Permission \"${permission}\" granted`\r\n : `Permission \"${permission}\" denied`,\r\n });\r\n}\r\n","/**\r\n * Secure Role Guard - React Provider\r\n *\r\n * Main provider component for permission management.\r\n * Framework-agnostic: works with Next.js, Remix, Gatsby, CRA, Vite.\r\n */\r\n\r\nimport { useMemo, type ReactNode } from \"react\";\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\r\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\r\n\r\n/**\r\n * Props for the PermissionProvider component.\r\n */\r\nexport type PermissionProviderProps = {\r\n /** Current user context (from your auth system) */\r\n readonly user: UserContext | null;\r\n /** Role registry created by defineRoles() */\r\n readonly registry: RoleRegistry;\r\n /** Child components */\r\n readonly children: ReactNode;\r\n};\r\n\r\n/**\r\n * Provides permission context to all child components.\r\n *\r\n * SECURITY: If user is null/undefined, all permission checks will deny by default.\r\n *\r\n * @example\r\n * ```tsx\r\n * import { defineRoles, PermissionProvider } from 'secure-role-guard';\r\n *\r\n * const roleRegistry = defineRoles({\r\n * admin: ['user.read', 'user.update'],\r\n * viewer: ['user.read']\r\n * });\r\n *\r\n * function App() {\r\n * const user = { roles: ['admin'] }; // From your auth system\r\n *\r\n * return (\r\n * <PermissionProvider user={user} registry={roleRegistry}>\r\n * <YourApp />\r\n * </PermissionProvider>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function PermissionProvider({\r\n user,\r\n registry,\r\n children,\r\n}: PermissionProviderProps): ReactNode {\r\n // Memoize context value to prevent unnecessary re-renders\r\n const contextValue = useMemo<PermissionContextValue>(\r\n () => ({\r\n user,\r\n registry,\r\n can: (permission: string) => canUser(user, permission, registry),\r\n canAll: (permissions: readonly string[]) =>\r\n canUserAll(user, permissions, registry),\r\n canAny: (permissions: readonly string[]) =>\r\n canUserAny(user, permissions, registry),\r\n }),\r\n [user, registry]\r\n );\r\n\r\n return (\r\n <PermissionContext.Provider value={contextValue}>\r\n {children}\r\n </PermissionContext.Provider>\r\n );\r\n}\r\n","/**\r\n * Secure Role Guard - React Hooks\r\n *\r\n * Hooks for permission checking in React components.\r\n * Framework-agnostic: works with any React framework.\r\n */\r\n\r\nimport { useContext } from \"react\";\r\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\r\n\r\n/**\r\n * Hook to access the full permission context.\r\n *\r\n * @returns Full PermissionContextValue\r\n *\r\n * @example\r\n * ```tsx\r\n * function UserProfile() {\r\n * const { user, can, canAll } = usePermissions();\r\n *\r\n * if (can('user.read')) {\r\n * return <UserDetails />;\r\n * }\r\n * return <AccessDenied />;\r\n * }\r\n * ```\r\n */\r\nexport function usePermissions(): PermissionContextValue {\r\n return useContext(PermissionContext);\r\n}\r\n\r\n/**\r\n * Hook to check a single permission.\r\n *\r\n * @param permission - The permission to check\r\n * @returns True if user has the permission\r\n *\r\n * @example\r\n * ```tsx\r\n * function EditButton() {\r\n * const canEdit = useCan('user.update');\r\n *\r\n * if (!canEdit) return null;\r\n * return <button>Edit User</button>;\r\n * }\r\n * ```\r\n */\r\nexport function useCan(permission: string): boolean {\r\n const { can } = useContext(PermissionContext);\r\n return can(permission);\r\n}\r\n\r\n/**\r\n * Hook to check if user has ALL specified permissions.\r\n *\r\n * @param permissions - Array of permissions to check\r\n * @returns True if user has all permissions\r\n *\r\n * @example\r\n * ```tsx\r\n * function AdminPanel() {\r\n * const canManage = useCanAll(['user.read', 'user.update', 'user.delete']);\r\n *\r\n * if (!canManage) return <AccessDenied />;\r\n * return <AdminDashboard />;\r\n * }\r\n * ```\r\n */\r\nexport function useCanAll(permissions: readonly string[]): boolean {\r\n const { canAll } = useContext(PermissionContext);\r\n return canAll(permissions);\r\n}\r\n\r\n/**\r\n * Hook to check if user has ANY of the specified permissions.\r\n *\r\n * @param permissions - Array of permissions to check\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * ```tsx\r\n * function ViewReports() {\r\n * const canView = useCanAny(['report.admin', 'report.viewer']);\r\n *\r\n * if (!canView) return null;\r\n * return <ReportList />;\r\n * }\r\n * ```\r\n */\r\nexport function useCanAny(permissions: readonly string[]): boolean {\r\n const { canAny } = useContext(PermissionContext);\r\n return canAny(permissions);\r\n}\r\n\r\n/**\r\n * Hook to get current user context.\r\n *\r\n * @returns Current UserContext or null\r\n *\r\n * @example\r\n * ```tsx\r\n * function UserInfo() {\r\n * const user = useUser();\r\n * return <span>User ID: {user?.userId ?? 'Anonymous'}</span>;\r\n * }\r\n * ```\r\n */\r\nexport function useUser() {\r\n const { user } = useContext(PermissionContext);\r\n return user;\r\n}\r\n","/**\r\n * Secure Role Guard - React Components\r\n *\r\n * Declarative components for conditional rendering based on permissions.\r\n * Framework-agnostic: works with any React framework.\r\n */\r\n\r\nimport type { ReactNode } from \"react\";\r\nimport { useCan, useCanAll, useCanAny } from \"./hooks\";\r\n\r\n/**\r\n * Props for the Can component.\r\n */\r\nexport type CanProps = {\r\n /** Single permission to check */\r\n readonly permission?: string;\r\n /** Multiple permissions - user must have ALL (default behavior) */\r\n readonly permissions?: readonly string[];\r\n /** If true, user needs only ANY of the permissions */\r\n readonly anyOf?: boolean;\r\n /** Content to render if permission granted */\r\n readonly children: ReactNode;\r\n /** Optional fallback content if permission denied */\r\n readonly fallback?: ReactNode;\r\n};\r\n\r\n/**\r\n * Conditionally renders children based on user permissions.\r\n *\r\n * SECURITY: If no permission matches, nothing is rendered (deny by default).\r\n *\r\n * @example Single permission\r\n * ```tsx\r\n * <Can permission=\"user.update\">\r\n * <EditButton />\r\n * </Can>\r\n * ```\r\n *\r\n * @example Multiple permissions (ALL required)\r\n * ```tsx\r\n * <Can permissions={['user.read', 'user.update']}>\r\n * <UserEditor />\r\n * </Can>\r\n * ```\r\n *\r\n * @example Multiple permissions (ANY required)\r\n * ```tsx\r\n * <Can permissions={['admin', 'moderator']} anyOf>\r\n * <ModPanel />\r\n * </Can>\r\n * ```\r\n *\r\n * @example With fallback\r\n * ```tsx\r\n * <Can permission=\"premium.access\" fallback={<UpgradePrompt />}>\r\n * <PremiumContent />\r\n * </Can>\r\n * ```\r\n */\r\nexport function Can({\r\n permission,\r\n permissions,\r\n anyOf = false,\r\n children,\r\n fallback = null,\r\n}: CanProps): ReactNode {\r\n // Single permission check\r\n if (permission !== undefined) {\r\n const allowed = useCan(permission);\r\n return allowed ? children : fallback;\r\n }\r\n\r\n // Multiple permissions check\r\n if (permissions !== undefined && permissions.length > 0) {\r\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\r\n return allowed ? children : fallback;\r\n }\r\n\r\n // No permissions specified - deny by default\r\n return fallback;\r\n}\r\n\r\n/**\r\n * Props for the Cannot component.\r\n */\r\nexport type CannotProps = {\r\n /** Single permission to check */\r\n readonly permission?: string;\r\n /** Multiple permissions to check */\r\n readonly permissions?: readonly string[];\r\n /** If true, blocked when user has ANY permission */\r\n readonly anyOf?: boolean;\r\n /** Content to render if permission is NOT granted */\r\n readonly children: ReactNode;\r\n};\r\n\r\n/**\r\n * Inverse of Can - renders children when user does NOT have the permission.\r\n * Useful for showing upgrade prompts, locked features, etc.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Cannot permission=\"premium.access\">\r\n * <UpgradePrompt />\r\n * </Cannot>\r\n * ```\r\n */\r\nexport function Cannot({\r\n permission,\r\n permissions,\r\n anyOf = false,\r\n children,\r\n}: CannotProps): ReactNode {\r\n // Single permission check\r\n if (permission !== undefined) {\r\n const allowed = useCan(permission);\r\n return allowed ? null : children;\r\n }\r\n\r\n // Multiple permissions check\r\n if (permissions !== undefined && permissions.length > 0) {\r\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\r\n return allowed ? null : children;\r\n }\r\n\r\n // No permissions specified - show children (inverse of deny by default)\r\n return children;\r\n}\r\n"]}
@@ -0,0 +1,167 @@
1
+ import { createContext, useMemo, useContext } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ // src/react/context.tsx
5
+ var defaultContextValue = {
6
+ user: null,
7
+ registry: {
8
+ getPermissions: () => Object.freeze([]),
9
+ hasRole: () => false,
10
+ getRoleNames: () => Object.freeze([])
11
+ },
12
+ can: () => false,
13
+ canAll: () => false,
14
+ canAny: () => false
15
+ };
16
+ var PermissionContext = createContext(defaultContextValue);
17
+ PermissionContext.displayName = "PermissionContext";
18
+
19
+ // src/core/permission-engine.ts
20
+ var WILDCARD_PERMISSION = "*";
21
+ function collectUserPermissions(user, registry) {
22
+ const permissions = /* @__PURE__ */ new Set();
23
+ if (user.permissions !== void 0) {
24
+ for (const permission of user.permissions) {
25
+ permissions.add(permission);
26
+ }
27
+ }
28
+ if (user.roles !== void 0) {
29
+ for (const role of user.roles) {
30
+ const rolePermissions = registry.getPermissions(role);
31
+ for (const permission of rolePermissions) {
32
+ permissions.add(permission);
33
+ }
34
+ }
35
+ }
36
+ return permissions;
37
+ }
38
+ function hasWildcardAccess(permissions, requested) {
39
+ if (permissions.has(WILDCARD_PERMISSION)) {
40
+ return true;
41
+ }
42
+ const parts = requested.split(".");
43
+ let namespace = "";
44
+ for (let i = 0; i < parts.length - 1; i++) {
45
+ const part = parts[i];
46
+ if (part !== void 0) {
47
+ namespace = namespace === "" ? part : `${namespace}.${part}`;
48
+ if (permissions.has(`${namespace}.*`)) {
49
+ return true;
50
+ }
51
+ }
52
+ }
53
+ return false;
54
+ }
55
+ function canUser(user, permission, registry) {
56
+ if (user === null || user === void 0) {
57
+ return false;
58
+ }
59
+ if (permission === "" || permission.trim() === "") {
60
+ return false;
61
+ }
62
+ const userPermissions = collectUserPermissions(user, registry);
63
+ if (userPermissions.has(permission)) {
64
+ return true;
65
+ }
66
+ if (hasWildcardAccess(userPermissions, permission)) {
67
+ return true;
68
+ }
69
+ return false;
70
+ }
71
+ function canUserAll(user, permissions, registry) {
72
+ if (permissions.length === 0) {
73
+ return false;
74
+ }
75
+ for (const permission of permissions) {
76
+ if (!canUser(user, permission, registry)) {
77
+ return false;
78
+ }
79
+ }
80
+ return true;
81
+ }
82
+ function canUserAny(user, permissions, registry) {
83
+ if (permissions.length === 0) {
84
+ return false;
85
+ }
86
+ for (const permission of permissions) {
87
+ if (canUser(user, permission, registry)) {
88
+ return true;
89
+ }
90
+ }
91
+ return false;
92
+ }
93
+ function PermissionProvider({
94
+ user,
95
+ registry,
96
+ children
97
+ }) {
98
+ const contextValue = useMemo(
99
+ () => ({
100
+ user,
101
+ registry,
102
+ can: (permission) => canUser(user, permission, registry),
103
+ canAll: (permissions) => canUserAll(user, permissions, registry),
104
+ canAny: (permissions) => canUserAny(user, permissions, registry)
105
+ }),
106
+ [user, registry]
107
+ );
108
+ return /* @__PURE__ */ jsx(PermissionContext.Provider, { value: contextValue, children });
109
+ }
110
+ function usePermissions() {
111
+ return useContext(PermissionContext);
112
+ }
113
+ function useCan(permission) {
114
+ const { can } = useContext(PermissionContext);
115
+ return can(permission);
116
+ }
117
+ function useCanAll(permissions) {
118
+ const { canAll } = useContext(PermissionContext);
119
+ return canAll(permissions);
120
+ }
121
+ function useCanAny(permissions) {
122
+ const { canAny } = useContext(PermissionContext);
123
+ return canAny(permissions);
124
+ }
125
+ function useUser() {
126
+ const { user } = useContext(PermissionContext);
127
+ return user;
128
+ }
129
+
130
+ // src/react/components.tsx
131
+ function Can({
132
+ permission,
133
+ permissions,
134
+ anyOf = false,
135
+ children,
136
+ fallback = null
137
+ }) {
138
+ if (permission !== void 0) {
139
+ const allowed = useCan(permission);
140
+ return allowed ? children : fallback;
141
+ }
142
+ if (permissions !== void 0 && permissions.length > 0) {
143
+ const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);
144
+ return allowed ? children : fallback;
145
+ }
146
+ return fallback;
147
+ }
148
+ function Cannot({
149
+ permission,
150
+ permissions,
151
+ anyOf = false,
152
+ children
153
+ }) {
154
+ if (permission !== void 0) {
155
+ const allowed = useCan(permission);
156
+ return allowed ? null : children;
157
+ }
158
+ if (permissions !== void 0 && permissions.length > 0) {
159
+ const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);
160
+ return allowed ? null : children;
161
+ }
162
+ return children;
163
+ }
164
+
165
+ export { Can, Cannot, PermissionContext, PermissionProvider, useCan, useCanAll, useCanAny, usePermissions, useUser };
166
+ //# sourceMappingURL=index.mjs.map
167
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/context.tsx","../../src/core/permission-engine.ts","../../src/react/provider.tsx","../../src/react/hooks.ts","../../src/react/components.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * Secure Role Guard - React Context\r\n *\r\n * Framework-agnostic React context for permission management.\r\n * Works with Next.js, Remix, Gatsby, CRA, Vite, and any React framework.\r\n */\r\n\r\nimport { createContext } from \"react\";\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\n\r\n/**\r\n * Shape of the permission context value.\r\n */\r\nexport type PermissionContextValue = {\r\n /** Current user context */\r\n readonly user: UserContext | null;\r\n /** Role registry for permission lookups */\r\n readonly registry: RoleRegistry;\r\n /** Check if user has a specific permission */\r\n readonly can: (permission: string) => boolean;\r\n /** Check if user has ALL permissions */\r\n readonly canAll: (permissions: readonly string[]) => boolean;\r\n /** Check if user has ANY permission */\r\n readonly canAny: (permissions: readonly string[]) => boolean;\r\n};\r\n\r\n/**\r\n * Default context value (deny all).\r\n * Used when no provider is present.\r\n */\r\nconst defaultContextValue: PermissionContextValue = {\r\n user: null,\r\n registry: {\r\n getPermissions: () => Object.freeze([]),\r\n hasRole: () => false,\r\n getRoleNames: () => Object.freeze([]),\r\n },\r\n can: () => false,\r\n canAll: () => false,\r\n canAny: () => false,\r\n};\r\n\r\n/**\r\n * React context for permissions.\r\n * Default value denies all permissions (deny by default).\r\n */\r\nexport const PermissionContext =\r\n createContext<PermissionContextValue>(defaultContextValue);\r\n\r\n// Set display name for React DevTools\r\nPermissionContext.displayName = \"PermissionContext\";\r\n","/**\r\n * Secure Role Guard - Permission Engine\r\n *\r\n * Pure functions for permission checking.\r\n * Zero side effects, zero dependencies, zero mutations.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\r\n\r\n/** Wildcard permission that grants all access */\r\nconst WILDCARD_PERMISSION = \"*\";\r\n\r\n/**\r\n * Collects all permissions for a user from their roles and direct permissions.\r\n *\r\n * @param user - The user context\r\n * @param registry - The role registry to resolve role permissions\r\n * @returns Set of all permissions (for efficient lookup)\r\n */\r\nfunction collectUserPermissions(\r\n user: UserContext,\r\n registry: RoleRegistry\r\n): ReadonlySet<string> {\r\n const permissions = new Set<string>();\r\n\r\n // Add direct user permissions\r\n if (user.permissions !== undefined) {\r\n for (const permission of user.permissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n\r\n // Add permissions from all user roles\r\n if (user.roles !== undefined) {\r\n for (const role of user.roles) {\r\n const rolePermissions = registry.getPermissions(role);\r\n for (const permission of rolePermissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n }\r\n\r\n return permissions;\r\n}\r\n\r\n/**\r\n * Checks if a permission set contains a wildcard that grants the requested permission.\r\n *\r\n * Supports:\r\n * - Exact wildcard (*) - grants everything\r\n * - Namespace wildcards (user.*) - grants all under namespace\r\n *\r\n * @param permissions - Set of permissions to check\r\n * @param requested - The permission being requested\r\n * @returns True if wildcard grants access\r\n */\r\nfunction hasWildcardAccess(\r\n permissions: ReadonlySet<string>,\r\n requested: string\r\n): boolean {\r\n // Check for global wildcard\r\n if (permissions.has(WILDCARD_PERMISSION)) {\r\n return true;\r\n }\r\n\r\n // Check for namespace wildcards (e.g., user.* grants user.read)\r\n const parts = requested.split(\".\");\r\n let namespace = \"\";\r\n\r\n for (let i = 0; i < parts.length - 1; i++) {\r\n const part = parts[i];\r\n if (part !== undefined) {\r\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\r\n if (permissions.has(`${namespace}.*`)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has a specific permission.\r\n *\r\n * SECURITY GUARANTEES:\r\n * - Deny by default: undefined/null/empty always returns false\r\n * - Pure function: no side effects, no mutations\r\n * - Deterministic: same inputs always produce same output\r\n *\r\n * @param user - The user context to check\r\n * @param permission - The permission required\r\n * @param registry - The role registry for resolving roles\r\n * @returns True if user has the permission, false otherwise\r\n *\r\n * @example\r\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\r\n */\r\nexport function canUser(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: No user context means no access\r\n if (user === null || user === undefined) {\r\n return false;\r\n }\r\n\r\n // DENY BY DEFAULT: Empty permission request is invalid\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return false;\r\n }\r\n\r\n const userPermissions = collectUserPermissions(user, registry);\r\n\r\n // Check for exact permission match\r\n if (userPermissions.has(permission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard access\r\n if (hasWildcardAccess(userPermissions, permission)) {\r\n return true;\r\n }\r\n\r\n // DENY BY DEFAULT\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions required\r\n * @param registry - The role registry\r\n * @returns True if user has ALL permissions\r\n *\r\n * @example\r\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\r\n */\r\nexport function canUserAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (!canUser(user, permission, registry)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions to check\r\n * @param registry - The role registry\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\r\n */\r\nexport function canUserAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (canUser(user, permission, registry)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Extended permission check with detailed result.\r\n * Useful for debugging and logging.\r\n *\r\n * @param user - The user context\r\n * @param permission - Permission to check\r\n * @param registry - The role registry\r\n * @returns PermissionCheckResult with allowed status and reason\r\n */\r\nexport function checkPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionCheckResult {\r\n if (user === null || user === undefined) {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"No user context provided\",\r\n });\r\n }\r\n\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"Empty permission requested\",\r\n });\r\n }\r\n\r\n const allowed = canUser(user, permission, registry);\r\n\r\n return Object.freeze({\r\n allowed,\r\n reason: allowed\r\n ? `Permission \"${permission}\" granted`\r\n : `Permission \"${permission}\" denied`,\r\n });\r\n}\r\n","/**\r\n * Secure Role Guard - React Provider\r\n *\r\n * Main provider component for permission management.\r\n * Framework-agnostic: works with Next.js, Remix, Gatsby, CRA, Vite.\r\n */\r\n\r\nimport { useMemo, type ReactNode } from \"react\";\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\r\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\r\n\r\n/**\r\n * Props for the PermissionProvider component.\r\n */\r\nexport type PermissionProviderProps = {\r\n /** Current user context (from your auth system) */\r\n readonly user: UserContext | null;\r\n /** Role registry created by defineRoles() */\r\n readonly registry: RoleRegistry;\r\n /** Child components */\r\n readonly children: ReactNode;\r\n};\r\n\r\n/**\r\n * Provides permission context to all child components.\r\n *\r\n * SECURITY: If user is null/undefined, all permission checks will deny by default.\r\n *\r\n * @example\r\n * ```tsx\r\n * import { defineRoles, PermissionProvider } from 'secure-role-guard';\r\n *\r\n * const roleRegistry = defineRoles({\r\n * admin: ['user.read', 'user.update'],\r\n * viewer: ['user.read']\r\n * });\r\n *\r\n * function App() {\r\n * const user = { roles: ['admin'] }; // From your auth system\r\n *\r\n * return (\r\n * <PermissionProvider user={user} registry={roleRegistry}>\r\n * <YourApp />\r\n * </PermissionProvider>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function PermissionProvider({\r\n user,\r\n registry,\r\n children,\r\n}: PermissionProviderProps): ReactNode {\r\n // Memoize context value to prevent unnecessary re-renders\r\n const contextValue = useMemo<PermissionContextValue>(\r\n () => ({\r\n user,\r\n registry,\r\n can: (permission: string) => canUser(user, permission, registry),\r\n canAll: (permissions: readonly string[]) =>\r\n canUserAll(user, permissions, registry),\r\n canAny: (permissions: readonly string[]) =>\r\n canUserAny(user, permissions, registry),\r\n }),\r\n [user, registry]\r\n );\r\n\r\n return (\r\n <PermissionContext.Provider value={contextValue}>\r\n {children}\r\n </PermissionContext.Provider>\r\n );\r\n}\r\n","/**\r\n * Secure Role Guard - React Hooks\r\n *\r\n * Hooks for permission checking in React components.\r\n * Framework-agnostic: works with any React framework.\r\n */\r\n\r\nimport { useContext } from \"react\";\r\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\r\n\r\n/**\r\n * Hook to access the full permission context.\r\n *\r\n * @returns Full PermissionContextValue\r\n *\r\n * @example\r\n * ```tsx\r\n * function UserProfile() {\r\n * const { user, can, canAll } = usePermissions();\r\n *\r\n * if (can('user.read')) {\r\n * return <UserDetails />;\r\n * }\r\n * return <AccessDenied />;\r\n * }\r\n * ```\r\n */\r\nexport function usePermissions(): PermissionContextValue {\r\n return useContext(PermissionContext);\r\n}\r\n\r\n/**\r\n * Hook to check a single permission.\r\n *\r\n * @param permission - The permission to check\r\n * @returns True if user has the permission\r\n *\r\n * @example\r\n * ```tsx\r\n * function EditButton() {\r\n * const canEdit = useCan('user.update');\r\n *\r\n * if (!canEdit) return null;\r\n * return <button>Edit User</button>;\r\n * }\r\n * ```\r\n */\r\nexport function useCan(permission: string): boolean {\r\n const { can } = useContext(PermissionContext);\r\n return can(permission);\r\n}\r\n\r\n/**\r\n * Hook to check if user has ALL specified permissions.\r\n *\r\n * @param permissions - Array of permissions to check\r\n * @returns True if user has all permissions\r\n *\r\n * @example\r\n * ```tsx\r\n * function AdminPanel() {\r\n * const canManage = useCanAll(['user.read', 'user.update', 'user.delete']);\r\n *\r\n * if (!canManage) return <AccessDenied />;\r\n * return <AdminDashboard />;\r\n * }\r\n * ```\r\n */\r\nexport function useCanAll(permissions: readonly string[]): boolean {\r\n const { canAll } = useContext(PermissionContext);\r\n return canAll(permissions);\r\n}\r\n\r\n/**\r\n * Hook to check if user has ANY of the specified permissions.\r\n *\r\n * @param permissions - Array of permissions to check\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * ```tsx\r\n * function ViewReports() {\r\n * const canView = useCanAny(['report.admin', 'report.viewer']);\r\n *\r\n * if (!canView) return null;\r\n * return <ReportList />;\r\n * }\r\n * ```\r\n */\r\nexport function useCanAny(permissions: readonly string[]): boolean {\r\n const { canAny } = useContext(PermissionContext);\r\n return canAny(permissions);\r\n}\r\n\r\n/**\r\n * Hook to get current user context.\r\n *\r\n * @returns Current UserContext or null\r\n *\r\n * @example\r\n * ```tsx\r\n * function UserInfo() {\r\n * const user = useUser();\r\n * return <span>User ID: {user?.userId ?? 'Anonymous'}</span>;\r\n * }\r\n * ```\r\n */\r\nexport function useUser() {\r\n const { user } = useContext(PermissionContext);\r\n return user;\r\n}\r\n","/**\r\n * Secure Role Guard - React Components\r\n *\r\n * Declarative components for conditional rendering based on permissions.\r\n * Framework-agnostic: works with any React framework.\r\n */\r\n\r\nimport type { ReactNode } from \"react\";\r\nimport { useCan, useCanAll, useCanAny } from \"./hooks\";\r\n\r\n/**\r\n * Props for the Can component.\r\n */\r\nexport type CanProps = {\r\n /** Single permission to check */\r\n readonly permission?: string;\r\n /** Multiple permissions - user must have ALL (default behavior) */\r\n readonly permissions?: readonly string[];\r\n /** If true, user needs only ANY of the permissions */\r\n readonly anyOf?: boolean;\r\n /** Content to render if permission granted */\r\n readonly children: ReactNode;\r\n /** Optional fallback content if permission denied */\r\n readonly fallback?: ReactNode;\r\n};\r\n\r\n/**\r\n * Conditionally renders children based on user permissions.\r\n *\r\n * SECURITY: If no permission matches, nothing is rendered (deny by default).\r\n *\r\n * @example Single permission\r\n * ```tsx\r\n * <Can permission=\"user.update\">\r\n * <EditButton />\r\n * </Can>\r\n * ```\r\n *\r\n * @example Multiple permissions (ALL required)\r\n * ```tsx\r\n * <Can permissions={['user.read', 'user.update']}>\r\n * <UserEditor />\r\n * </Can>\r\n * ```\r\n *\r\n * @example Multiple permissions (ANY required)\r\n * ```tsx\r\n * <Can permissions={['admin', 'moderator']} anyOf>\r\n * <ModPanel />\r\n * </Can>\r\n * ```\r\n *\r\n * @example With fallback\r\n * ```tsx\r\n * <Can permission=\"premium.access\" fallback={<UpgradePrompt />}>\r\n * <PremiumContent />\r\n * </Can>\r\n * ```\r\n */\r\nexport function Can({\r\n permission,\r\n permissions,\r\n anyOf = false,\r\n children,\r\n fallback = null,\r\n}: CanProps): ReactNode {\r\n // Single permission check\r\n if (permission !== undefined) {\r\n const allowed = useCan(permission);\r\n return allowed ? children : fallback;\r\n }\r\n\r\n // Multiple permissions check\r\n if (permissions !== undefined && permissions.length > 0) {\r\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\r\n return allowed ? children : fallback;\r\n }\r\n\r\n // No permissions specified - deny by default\r\n return fallback;\r\n}\r\n\r\n/**\r\n * Props for the Cannot component.\r\n */\r\nexport type CannotProps = {\r\n /** Single permission to check */\r\n readonly permission?: string;\r\n /** Multiple permissions to check */\r\n readonly permissions?: readonly string[];\r\n /** If true, blocked when user has ANY permission */\r\n readonly anyOf?: boolean;\r\n /** Content to render if permission is NOT granted */\r\n readonly children: ReactNode;\r\n};\r\n\r\n/**\r\n * Inverse of Can - renders children when user does NOT have the permission.\r\n * Useful for showing upgrade prompts, locked features, etc.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Cannot permission=\"premium.access\">\r\n * <UpgradePrompt />\r\n * </Cannot>\r\n * ```\r\n */\r\nexport function Cannot({\r\n permission,\r\n permissions,\r\n anyOf = false,\r\n children,\r\n}: CannotProps): ReactNode {\r\n // Single permission check\r\n if (permission !== undefined) {\r\n const allowed = useCan(permission);\r\n return allowed ? null : children;\r\n }\r\n\r\n // Multiple permissions check\r\n if (permissions !== undefined && permissions.length > 0) {\r\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\r\n return allowed ? null : children;\r\n }\r\n\r\n // No permissions specified - show children (inverse of deny by default)\r\n return children;\r\n}\r\n"]}