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 @@
1
+ {"version":3,"file":"ruleEngine.d.ts","sourceRoot":"","sources":["../../src/core/ruleEngine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAEjB;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,KAAK,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG,EAC7D,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,SAAS,CAAC,EACtC,OAAO,EAAE,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,GAC3C,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG,EAC5D,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,kBAAkB,CAAC,KAAK,EAAE,SAAS,CAAC,EAC9C,OAAO,EAAE,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,GAC3C,cAAc,CAAC,KAAK,EAAE,SAAS,CAAC,CAUlC;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG,EACnE,KAAK,EAAE,eAAe,CAAC,KAAK,EAAE,SAAS,CAAC,EACxC,OAAO,EAAE,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,EAC5C,QAAQ,EAAE,kBAAkB,CAAC,KAAK,EAAE,SAAS,CAAC,EAC9C,IAAI,GAAE,KAAK,GAAG,KAAa,GAC1B,OAAO,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC,CAAC,CAgFD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,GAAG,GAAG,EAAE,SAAS,GAAG,GAAG,EAClE,IAAI,EAAE,KAAK,EACX,QAAQ,EAAE,SAAS,GAAG,SAAS,EAC/B,KAAK,EAAE,MAAM,EAAE,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,CAQrC"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Rule Engine
3
+ *
4
+ * Core logic for evaluating permission rules against a context.
5
+ * Supports sync/async rules, string-based rules, and inline functions.
6
+ */
7
+ /**
8
+ * Evaluates a single permission rule
9
+ *
10
+ * @param rule - The rule function to evaluate
11
+ * @param context - The permission context
12
+ * @returns Promise resolving to rule result and evaluation metadata
13
+ */
14
+ export async function evaluateRule(rule, context) {
15
+ const startTime = performance.now();
16
+ try {
17
+ const result = await Promise.resolve(rule(context));
18
+ const duration = performance.now() - startTime;
19
+ return {
20
+ result: Boolean(result),
21
+ duration,
22
+ };
23
+ }
24
+ catch (error) {
25
+ const duration = performance.now() - startTime;
26
+ return {
27
+ result: false,
28
+ duration,
29
+ error: error instanceof Error ? error.message : String(error),
30
+ };
31
+ }
32
+ }
33
+ /**
34
+ * Resolves a string-based permission key to a rule function
35
+ *
36
+ * Strategy:
37
+ * 1. Check if it exists in the rules map
38
+ * 2. Check if it's in the permissions array (direct permission grant)
39
+ * 3. Check if it's in the roles array (role-based grant)
40
+ * 4. Otherwise deny
41
+ */
42
+ export function resolveStringRule(permissionKey, rulesMap, context) {
43
+ // If a custom rule exists for this key, use it
44
+ if (rulesMap[permissionKey]) {
45
+ return rulesMap[permissionKey];
46
+ }
47
+ // Otherwise, check if the permission/role is directly granted
48
+ return (ctx) => {
49
+ return ctx.permissions.includes(permissionKey) || ctx.roles.includes(permissionKey);
50
+ };
51
+ }
52
+ /**
53
+ * Evaluates a permission check (string, array, or function)
54
+ *
55
+ * @param check - The permission check to evaluate
56
+ * @param context - The permission context
57
+ * @param rulesMap - Map of named rules
58
+ * @param mode - Evaluation mode: 'any' (OR) or 'all' (AND)
59
+ * @returns Promise resolving to evaluation result and metadata
60
+ */
61
+ export async function evaluatePermission(check, context, rulesMap, mode = 'any') {
62
+ const ruleResults = [];
63
+ // Case 1: Inline function rule
64
+ if (typeof check === 'function') {
65
+ const evaluation = await evaluateRule(check, context);
66
+ ruleResults.push({
67
+ rule: 'inline',
68
+ result: evaluation.result,
69
+ duration: evaluation.duration,
70
+ error: evaluation.error,
71
+ });
72
+ return {
73
+ allowed: evaluation.result,
74
+ ruleResults,
75
+ };
76
+ }
77
+ // Case 2: Single string permission
78
+ if (typeof check === 'string') {
79
+ const rule = resolveStringRule(check, rulesMap, context);
80
+ const evaluation = await evaluateRule(rule, context);
81
+ ruleResults.push({
82
+ rule: check,
83
+ result: evaluation.result,
84
+ duration: evaluation.duration,
85
+ error: evaluation.error,
86
+ });
87
+ return {
88
+ allowed: evaluation.result,
89
+ ruleResults,
90
+ };
91
+ }
92
+ // Case 3: Array of permission strings
93
+ if (Array.isArray(check)) {
94
+ const evaluations = await Promise.all(check.map(async (key) => {
95
+ const rule = resolveStringRule(key, rulesMap, context);
96
+ const evaluation = await evaluateRule(rule, context);
97
+ return {
98
+ rule: key,
99
+ result: evaluation.result,
100
+ duration: evaluation.duration,
101
+ error: evaluation.error,
102
+ };
103
+ }));
104
+ ruleResults.push(...evaluations);
105
+ // Apply mode logic
106
+ const allowed = mode === 'all'
107
+ ? evaluations.every((e) => e.result)
108
+ : evaluations.some((e) => e.result);
109
+ return {
110
+ allowed,
111
+ ruleResults,
112
+ };
113
+ }
114
+ // Invalid check type
115
+ return {
116
+ allowed: false,
117
+ ruleResults: [
118
+ {
119
+ rule: 'unknown',
120
+ result: false,
121
+ duration: 0,
122
+ error: 'Invalid permission check type',
123
+ },
124
+ ],
125
+ };
126
+ }
127
+ /**
128
+ * Creates a permission context from configuration values
129
+ */
130
+ export function createPermissionContext(user, resource, roles, permissions, flags) {
131
+ return {
132
+ user,
133
+ resource,
134
+ roles,
135
+ permissions,
136
+ flags,
137
+ };
138
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Core types for react-permissions-gate
3
+ *
4
+ * Defines the type system for permission checking, rule evaluation,
5
+ * and context management across the library.
6
+ */
7
+ /**
8
+ * Permission evaluation context passed to rule functions
9
+ */
10
+ export interface PermissionContext<TUser = any, TResource = any> {
11
+ /** Current authenticated user */
12
+ user: TUser;
13
+ /** Optional resource being accessed */
14
+ resource?: TResource;
15
+ /** Array of role strings assigned to the user */
16
+ roles: string[];
17
+ /** Array of permission strings granted to the user */
18
+ permissions: string[];
19
+ /** Feature flags map */
20
+ flags: Record<string, boolean>;
21
+ }
22
+ /**
23
+ * Permission rule function signature
24
+ * Can return boolean synchronously or Promise<boolean> for async checks
25
+ */
26
+ export type PermissionRule<TUser = any, TResource = any> = (ctx: PermissionContext<TUser, TResource>) => boolean | Promise<boolean>;
27
+ /**
28
+ * Map of named permission rules
29
+ */
30
+ export type PermissionRulesMap<TUser = any, TResource = any> = Record<string, PermissionRule<TUser, TResource>>;
31
+ /**
32
+ * Permission check input - can be a rule key, array of keys, or inline function
33
+ */
34
+ export type PermissionCheck<TUser = any, TResource = any> = string | string[] | PermissionRule<TUser, TResource>;
35
+ /**
36
+ * Mode for rendering behavior when permission is denied
37
+ */
38
+ export type PermissionMode = 'hide' | 'disable';
39
+ /**
40
+ * Configuration for the PermissionsProvider
41
+ */
42
+ export interface PermissionsConfig<TUser = any> {
43
+ /** Current authenticated user */
44
+ user: TUser;
45
+ /** Array of role strings */
46
+ roles?: string[];
47
+ /** Array of permission strings */
48
+ permissions?: string[];
49
+ /** Named permission rules */
50
+ rules?: PermissionRulesMap<TUser, any>;
51
+ /** Feature flags */
52
+ flags?: Record<string, boolean>;
53
+ /** Enable dev tools panel (defaults to process.env.NODE_ENV !== 'production') */
54
+ enableDevTools?: boolean;
55
+ }
56
+ /**
57
+ * Internal context value exposed by PermissionsProvider
58
+ */
59
+ export interface PermissionsContextValue<TUser = any> {
60
+ user: TUser;
61
+ roles: string[];
62
+ permissions: string[];
63
+ rules: PermissionRulesMap<TUser, any>;
64
+ flags: Record<string, boolean>;
65
+ enableDevTools: boolean;
66
+ /** Internal: Evaluate a permission check */
67
+ evaluatePermission: <TResource = any>(check: PermissionCheck<TUser, TResource>, resource?: TResource, mode?: 'any' | 'all') => Promise<boolean>;
68
+ /** Internal: Register permission evaluation for dev tools */
69
+ registerEvaluation?: (evaluation: PermissionEvaluation) => void;
70
+ }
71
+ /**
72
+ * Result of a permission evaluation (used by dev tools)
73
+ */
74
+ export interface PermissionEvaluation {
75
+ /** Unique ID for this evaluation */
76
+ id: string;
77
+ /** Timestamp of evaluation */
78
+ timestamp: number;
79
+ /** Permission check that was evaluated */
80
+ check: string | string[];
81
+ /** Resource involved (if any) */
82
+ resource?: any;
83
+ /** Result of the check */
84
+ allowed: boolean;
85
+ /** Details about which rules were evaluated */
86
+ ruleResults: RuleEvaluationResult[];
87
+ /** Component that triggered this check (if available) */
88
+ component?: string;
89
+ /** Evaluation mode (any/all) */
90
+ mode?: 'any' | 'all';
91
+ }
92
+ /**
93
+ * Result of evaluating a single rule
94
+ */
95
+ export interface RuleEvaluationResult {
96
+ /** Rule key or 'inline' for inline functions */
97
+ rule: string;
98
+ /** Result of the rule evaluation */
99
+ result: boolean;
100
+ /** Time taken to evaluate (ms) */
101
+ duration: number;
102
+ /** Error if rule threw */
103
+ error?: string;
104
+ }
105
+ /**
106
+ * Dev tools state
107
+ */
108
+ export interface DevToolsState {
109
+ /** All permission evaluations */
110
+ evaluations: PermissionEvaluation[];
111
+ /** Whether dev panel is open */
112
+ isOpen: boolean;
113
+ /** Override user context for testing */
114
+ overrideUser?: any;
115
+ /** Override roles for testing */
116
+ overrideRoles?: string[];
117
+ /** Override permissions for testing */
118
+ overridePermissions?: string[];
119
+ /** Override flags for testing */
120
+ overrideFlags?: Record<string, boolean>;
121
+ }
122
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Core types for react-permissions-gate
3
+ *
4
+ * Defines the type system for permission checking, rule evaluation,
5
+ * and context management across the library.
6
+ */
7
+ export {};
@@ -0,0 +1,13 @@
1
+ /**
2
+ * DevPanel Component
3
+ *
4
+ * The killer feature: automatic development panel for permission debugging.
5
+ * Shows all permission checks, allows context overrides, and provides live feedback.
6
+ */
7
+ import React from 'react';
8
+ /**
9
+ * DevPanel Component
10
+ * Automatically shown in development mode
11
+ */
12
+ export declare function DevPanel(): React.JSX.Element | null;
13
+ //# sourceMappingURL=DevPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DevPanel.d.ts","sourceRoot":"","sources":["../../src/devtools/DevPanel.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAA4B,MAAM,OAAO,CAAC;AAsJjD;;;GAGG;AACH,wBAAgB,QAAQ,6BA4GvB"}
@@ -0,0 +1,308 @@
1
+ /**
2
+ * DevPanel Component
3
+ *
4
+ * The killer feature: automatic development panel for permission debugging.
5
+ * Shows all permission checks, allows context overrides, and provides live feedback.
6
+ */
7
+ import React, { useState, useMemo } from 'react';
8
+ import { devStore } from './DevStore';
9
+ import { useDevToolsState } from './useDevRegister';
10
+ import { usePermissionsContext } from '../react/PermissionsProvider';
11
+ const PANEL_STYLES = {
12
+ container: {
13
+ position: 'fixed',
14
+ bottom: 0,
15
+ right: 0,
16
+ width: '450px',
17
+ maxHeight: '600px',
18
+ backgroundColor: '#1e1e1e',
19
+ color: '#d4d4d4',
20
+ fontFamily: 'monospace',
21
+ fontSize: '12px',
22
+ boxShadow: '0 -2px 10px rgba(0,0,0,0.5)',
23
+ zIndex: 999999,
24
+ display: 'flex',
25
+ flexDirection: 'column',
26
+ borderTopLeftRadius: '8px',
27
+ },
28
+ header: {
29
+ padding: '12px 16px',
30
+ backgroundColor: '#2d2d2d',
31
+ borderBottom: '1px solid #3e3e3e',
32
+ display: 'flex',
33
+ justifyContent: 'space-between',
34
+ alignItems: 'center',
35
+ borderTopLeftRadius: '8px',
36
+ },
37
+ title: {
38
+ margin: 0,
39
+ fontSize: '14px',
40
+ fontWeight: 'bold',
41
+ color: '#4fc3f7',
42
+ },
43
+ button: {
44
+ background: 'none',
45
+ border: '1px solid #4fc3f7',
46
+ color: '#4fc3f7',
47
+ padding: '4px 8px',
48
+ cursor: 'pointer',
49
+ fontSize: '11px',
50
+ borderRadius: '3px',
51
+ marginLeft: '8px',
52
+ },
53
+ content: {
54
+ flex: 1,
55
+ overflowY: 'auto',
56
+ padding: '16px',
57
+ },
58
+ tab: {
59
+ padding: '8px 16px',
60
+ cursor: 'pointer',
61
+ display: 'inline-block',
62
+ },
63
+ activeTab: {
64
+ borderBottom: '2px solid #4fc3f7',
65
+ color: '#4fc3f7',
66
+ },
67
+ tabs: {
68
+ display: 'flex',
69
+ backgroundColor: '#252525',
70
+ borderBottom: '1px solid #3e3e3e',
71
+ },
72
+ evaluation: {
73
+ marginBottom: '12px',
74
+ padding: '8px',
75
+ backgroundColor: '#252525',
76
+ borderRadius: '4px',
77
+ borderLeft: '3px solid',
78
+ },
79
+ allowed: {
80
+ borderLeftColor: '#4caf50',
81
+ },
82
+ denied: {
83
+ borderLeftColor: '#f44336',
84
+ },
85
+ timestamp: {
86
+ fontSize: '10px',
87
+ color: '#888',
88
+ },
89
+ badge: {
90
+ display: 'inline-block',
91
+ padding: '2px 6px',
92
+ borderRadius: '3px',
93
+ fontSize: '10px',
94
+ marginRight: '4px',
95
+ marginTop: '4px',
96
+ },
97
+ successBadge: {
98
+ backgroundColor: '#4caf50',
99
+ color: '#fff',
100
+ },
101
+ errorBadge: {
102
+ backgroundColor: '#f44336',
103
+ color: '#fff',
104
+ },
105
+ toggle: {
106
+ position: 'fixed',
107
+ bottom: '10px',
108
+ right: '10px',
109
+ width: '50px',
110
+ height: '50px',
111
+ borderRadius: '50%',
112
+ backgroundColor: '#4fc3f7',
113
+ color: '#fff',
114
+ border: 'none',
115
+ cursor: 'pointer',
116
+ fontSize: '20px',
117
+ boxShadow: '0 2px 10px rgba(0,0,0,0.3)',
118
+ zIndex: 999998,
119
+ display: 'flex',
120
+ alignItems: 'center',
121
+ justifyContent: 'center',
122
+ },
123
+ section: {
124
+ marginBottom: '16px',
125
+ },
126
+ sectionTitle: {
127
+ fontSize: '12px',
128
+ fontWeight: 'bold',
129
+ marginBottom: '8px',
130
+ color: '#4fc3f7',
131
+ },
132
+ checkbox: {
133
+ marginRight: '6px',
134
+ },
135
+ label: {
136
+ display: 'block',
137
+ padding: '4px 0',
138
+ cursor: 'pointer',
139
+ },
140
+ input: {
141
+ width: '100%',
142
+ padding: '6px',
143
+ backgroundColor: '#1e1e1e',
144
+ border: '1px solid #3e3e3e',
145
+ color: '#d4d4d4',
146
+ borderRadius: '3px',
147
+ marginTop: '4px',
148
+ fontFamily: 'monospace',
149
+ fontSize: '11px',
150
+ },
151
+ };
152
+ /**
153
+ * DevPanel Component
154
+ * Automatically shown in development mode
155
+ */
156
+ export function DevPanel() {
157
+ const state = useDevToolsState();
158
+ const context = usePermissionsContext();
159
+ const [activeTab, setActiveTab] = useState('evaluations');
160
+ if (!context.enableDevTools) {
161
+ return null;
162
+ }
163
+ // Apply overrides to current context
164
+ const effectiveRoles = state.overrideRoles || context.roles;
165
+ const effectivePermissions = state.overridePermissions || context.permissions;
166
+ const effectiveFlags = state.overrideFlags || context.flags;
167
+ return (React.createElement(React.Fragment, null,
168
+ !state.isOpen && (React.createElement("button", { style: PANEL_STYLES.toggle, onClick: () => devStore.togglePanel(), title: "Open Permissions Dev Panel" }, "\uD83D\uDD10")),
169
+ state.isOpen && (React.createElement("div", { style: PANEL_STYLES.container },
170
+ React.createElement("div", { style: PANEL_STYLES.header },
171
+ React.createElement("h3", { style: PANEL_STYLES.title }, "\uD83D\uDD10 Permissions Dev Panel"),
172
+ React.createElement("div", null,
173
+ React.createElement("button", { style: PANEL_STYLES.button, onClick: () => devStore.clearEvaluations() }, "Clear"),
174
+ React.createElement("button", { style: PANEL_STYLES.button, onClick: () => devStore.setOpen(false) }, "\u2715"))),
175
+ React.createElement("div", { style: PANEL_STYLES.tabs },
176
+ React.createElement("div", { style: {
177
+ ...PANEL_STYLES.tab,
178
+ ...(activeTab === 'evaluations' ? PANEL_STYLES.activeTab : {}),
179
+ }, onClick: () => setActiveTab('evaluations') },
180
+ "Evaluations (",
181
+ state.evaluations.length,
182
+ ")"),
183
+ React.createElement("div", { style: {
184
+ ...PANEL_STYLES.tab,
185
+ ...(activeTab === 'overrides' ? PANEL_STYLES.activeTab : {}),
186
+ }, onClick: () => setActiveTab('overrides') }, "Overrides"),
187
+ React.createElement("div", { style: {
188
+ ...PANEL_STYLES.tab,
189
+ ...(activeTab === 'context' ? PANEL_STYLES.activeTab : {}),
190
+ }, onClick: () => setActiveTab('context') }, "Context")),
191
+ React.createElement("div", { style: PANEL_STYLES.content },
192
+ activeTab === 'evaluations' && (React.createElement(EvaluationsTab, { evaluations: state.evaluations })),
193
+ activeTab === 'overrides' && (React.createElement(OverridesTab, { roles: context.roles, permissions: context.permissions, flags: context.flags, effectiveRoles: effectiveRoles, effectivePermissions: effectivePermissions, effectiveFlags: effectiveFlags })),
194
+ activeTab === 'context' && (React.createElement(ContextTab, { user: context.user, roles: effectiveRoles, permissions: effectivePermissions, flags: effectiveFlags })))))));
195
+ }
196
+ /**
197
+ * Evaluations tab content
198
+ */
199
+ function EvaluationsTab({ evaluations }) {
200
+ if (evaluations.length === 0) {
201
+ return React.createElement("div", { style: { color: '#888' } }, "No permission checks yet...");
202
+ }
203
+ return (React.createElement("div", null, evaluations.map((evaluation) => (React.createElement("div", { key: evaluation.id, style: {
204
+ ...PANEL_STYLES.evaluation,
205
+ ...(evaluation.allowed ? PANEL_STYLES.allowed : PANEL_STYLES.denied),
206
+ } },
207
+ React.createElement("div", { style: { marginBottom: '4px' } },
208
+ React.createElement("strong", { style: { color: evaluation.allowed ? '#4caf50' : '#f44336' } }, evaluation.allowed ? '✓ ALLOWED' : '✗ DENIED'),
209
+ React.createElement("span", { style: { marginLeft: '8px', color: '#d4d4d4' } }, typeof evaluation.check === 'string'
210
+ ? evaluation.check
211
+ : Array.isArray(evaluation.check)
212
+ ? evaluation.check.join(', ')
213
+ : 'inline function')),
214
+ React.createElement("div", { style: PANEL_STYLES.timestamp },
215
+ new Date(evaluation.timestamp).toLocaleTimeString(),
216
+ evaluation.mode && ` • mode: ${evaluation.mode}`),
217
+ React.createElement("div", { style: { marginTop: '6px' } }, evaluation.ruleResults.map((result, idx) => (React.createElement("span", { key: idx, style: {
218
+ ...PANEL_STYLES.badge,
219
+ ...(result.result ? PANEL_STYLES.successBadge : PANEL_STYLES.errorBadge),
220
+ }, title: `${result.duration.toFixed(2)}ms${result.error ? ` - ${result.error}` : ''}` },
221
+ result.rule,
222
+ ": ",
223
+ result.result ? '✓' : '✗')))),
224
+ evaluation.resource && (React.createElement("div", { style: { marginTop: '4px', fontSize: '10px', color: '#888' } },
225
+ "Resource: ",
226
+ JSON.stringify(evaluation.resource).slice(0, 50),
227
+ "...")))))));
228
+ }
229
+ /**
230
+ * Overrides tab content
231
+ */
232
+ function OverridesTab({ roles, permissions, flags, effectiveRoles, effectivePermissions, effectiveFlags, }) {
233
+ const [newRole, setNewRole] = useState('');
234
+ const [newPermission, setNewPermission] = useState('');
235
+ const [newFlag, setNewFlag] = useState('');
236
+ // Collect all unique roles and permissions from evaluations
237
+ const allRoles = useMemo(() => {
238
+ const rolesSet = new Set([...roles, ...effectiveRoles]);
239
+ return Array.from(rolesSet).sort();
240
+ }, [roles, effectiveRoles]);
241
+ const allPermissions = useMemo(() => {
242
+ const permsSet = new Set([...permissions, ...effectivePermissions]);
243
+ return Array.from(permsSet).sort();
244
+ }, [permissions, effectivePermissions]);
245
+ return (React.createElement("div", null,
246
+ React.createElement("button", { style: {
247
+ ...PANEL_STYLES.button,
248
+ width: '100%',
249
+ marginBottom: '16px',
250
+ }, onClick: () => devStore.resetOverrides() }, "Reset All Overrides"),
251
+ React.createElement("div", { style: PANEL_STYLES.section },
252
+ React.createElement("div", { style: PANEL_STYLES.sectionTitle }, "Roles"),
253
+ allRoles.map((role) => (React.createElement("label", { key: role, style: PANEL_STYLES.label },
254
+ React.createElement("input", { type: "checkbox", style: PANEL_STYLES.checkbox, checked: effectiveRoles.includes(role), onChange: () => devStore.toggleRole(role, roles) }),
255
+ role))),
256
+ React.createElement("input", { type: "text", style: PANEL_STYLES.input, placeholder: "Add new role...", value: newRole, onChange: (e) => setNewRole(e.target.value), onKeyPress: (e) => {
257
+ if (e.key === 'Enter' && newRole) {
258
+ devStore.toggleRole(newRole, roles);
259
+ setNewRole('');
260
+ }
261
+ } })),
262
+ React.createElement("div", { style: PANEL_STYLES.section },
263
+ React.createElement("div", { style: PANEL_STYLES.sectionTitle }, "Permissions"),
264
+ allPermissions.map((permission) => (React.createElement("label", { key: permission, style: PANEL_STYLES.label },
265
+ React.createElement("input", { type: "checkbox", style: PANEL_STYLES.checkbox, checked: effectivePermissions.includes(permission), onChange: () => devStore.togglePermission(permission, permissions) }),
266
+ permission))),
267
+ React.createElement("input", { type: "text", style: PANEL_STYLES.input, placeholder: "Add new permission...", value: newPermission, onChange: (e) => setNewPermission(e.target.value), onKeyPress: (e) => {
268
+ if (e.key === 'Enter' && newPermission) {
269
+ devStore.togglePermission(newPermission, permissions);
270
+ setNewPermission('');
271
+ }
272
+ } })),
273
+ React.createElement("div", { style: PANEL_STYLES.section },
274
+ React.createElement("div", { style: PANEL_STYLES.sectionTitle }, "Feature Flags"),
275
+ Object.entries(effectiveFlags).map(([flag, value]) => (React.createElement("label", { key: flag, style: PANEL_STYLES.label },
276
+ React.createElement("input", { type: "checkbox", style: PANEL_STYLES.checkbox, checked: value, onChange: () => devStore.toggleFlag(flag, flags) }),
277
+ flag))),
278
+ React.createElement("input", { type: "text", style: PANEL_STYLES.input, placeholder: "Add new flag...", value: newFlag, onChange: (e) => setNewFlag(e.target.value), onKeyPress: (e) => {
279
+ if (e.key === 'Enter' && newFlag) {
280
+ devStore.toggleFlag(newFlag, flags);
281
+ setNewFlag('');
282
+ }
283
+ } }))));
284
+ }
285
+ /**
286
+ * Context tab content
287
+ */
288
+ function ContextTab({ user, roles, permissions, flags, }) {
289
+ return (React.createElement("div", null,
290
+ React.createElement("div", { style: PANEL_STYLES.section },
291
+ React.createElement("div", { style: PANEL_STYLES.sectionTitle }, "User"),
292
+ React.createElement("pre", { style: { fontSize: '10px', overflow: 'auto' } }, JSON.stringify(user, null, 2))),
293
+ React.createElement("div", { style: PANEL_STYLES.section },
294
+ React.createElement("div", { style: PANEL_STYLES.sectionTitle },
295
+ "Roles (",
296
+ roles.length,
297
+ ")"),
298
+ React.createElement("div", null, roles.length > 0 ? roles.join(', ') : 'None')),
299
+ React.createElement("div", { style: PANEL_STYLES.section },
300
+ React.createElement("div", { style: PANEL_STYLES.sectionTitle },
301
+ "Permissions (",
302
+ permissions.length,
303
+ ")"),
304
+ React.createElement("div", null, permissions.length > 0 ? permissions.join(', ') : 'None')),
305
+ React.createElement("div", { style: PANEL_STYLES.section },
306
+ React.createElement("div", { style: PANEL_STYLES.sectionTitle }, "Feature Flags"),
307
+ React.createElement("pre", { style: { fontSize: '10px' } }, JSON.stringify(flags, null, 2)))));
308
+ }