@jmruthers/pace-core 0.5.109 → 0.5.111

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 (240) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/{AuthService-DrHrvXNZ.d.ts → AuthService-CVgsgtaZ.d.ts} +8 -0
  3. package/dist/{DataTable-5HITILXS.js → DataTable-5W2HVLLV.js} +8 -8
  4. package/dist/{UnifiedAuthProvider-A7I23UCN.js → UnifiedAuthProvider-LUM3QLS5.js} +3 -3
  5. package/dist/{api-5I3E47G2.js → api-SIZPFBFX.js} +5 -3
  6. package/dist/{audit-65VNHEV2.js → audit-5JI5T3SL.js} +2 -2
  7. package/dist/{chunk-P72NKAT5.js → chunk-2BIDKXQU.js} +157 -120
  8. package/dist/chunk-2BIDKXQU.js.map +1 -0
  9. package/dist/{chunk-S4D3Z723.js → chunk-ACYQNYHB.js} +7 -7
  10. package/dist/{chunk-D6MEKC27.js → chunk-EFVQBYFN.js} +2 -2
  11. package/dist/{chunk-EZ64QG2I.js → chunk-I5YM5BGS.js} +2 -2
  12. package/dist/{chunk-Q7APDV6H.js → chunk-IWJYNWXN.js} +13 -5
  13. package/dist/chunk-IWJYNWXN.js.map +1 -0
  14. package/dist/{chunk-YFMENCR4.js → chunk-JE2GFA3O.js} +3 -3
  15. package/dist/{chunk-AUXS7XSO.js → chunk-MW73E7SP.js} +35 -11
  16. package/dist/chunk-MW73E7SP.js.map +1 -0
  17. package/dist/{chunk-F6TSYCKP.js → chunk-PXXS26G5.js} +68 -29
  18. package/dist/chunk-PXXS26G5.js.map +1 -0
  19. package/dist/{chunk-UW2DE6JX.js → chunk-TD4BXGPE.js} +4 -4
  20. package/dist/{chunk-EYSXQ756.js → chunk-TDFBX7KJ.js} +2 -2
  21. package/dist/{chunk-WWNOVFDC.js → chunk-UGVU7L7N.js} +52 -90
  22. package/dist/chunk-UGVU7L7N.js.map +1 -0
  23. package/dist/{chunk-2W4WKJVF.js → chunk-X7SPKHYZ.js} +290 -255
  24. package/dist/chunk-X7SPKHYZ.js.map +1 -0
  25. package/dist/{chunk-3TKTL5AZ.js → chunk-ZL45MG76.js} +60 -60
  26. package/dist/chunk-ZL45MG76.js.map +1 -0
  27. package/dist/components.js +10 -10
  28. package/dist/hooks.d.ts +11 -1
  29. package/dist/hooks.js +9 -7
  30. package/dist/hooks.js.map +1 -1
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +13 -13
  33. package/dist/providers.d.ts +2 -2
  34. package/dist/providers.js +2 -2
  35. package/dist/rbac/index.d.ts +46 -29
  36. package/dist/rbac/index.js +9 -9
  37. package/dist/utils.js +1 -1
  38. package/docs/api/classes/ColumnFactory.md +1 -1
  39. package/docs/api/classes/ErrorBoundary.md +1 -1
  40. package/docs/api/classes/InvalidScopeError.md +4 -4
  41. package/docs/api/classes/MissingUserContextError.md +4 -4
  42. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  43. package/docs/api/classes/PermissionDeniedError.md +4 -4
  44. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  45. package/docs/api/classes/RBACAuditManager.md +8 -8
  46. package/docs/api/classes/RBACCache.md +8 -8
  47. package/docs/api/classes/RBACEngine.md +9 -8
  48. package/docs/api/classes/RBACError.md +4 -4
  49. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  50. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  51. package/docs/api/classes/StorageUtils.md +1 -1
  52. package/docs/api/enums/FileCategory.md +1 -1
  53. package/docs/api/interfaces/AggregateConfig.md +1 -1
  54. package/docs/api/interfaces/ButtonProps.md +1 -1
  55. package/docs/api/interfaces/CardProps.md +1 -1
  56. package/docs/api/interfaces/ColorPalette.md +1 -1
  57. package/docs/api/interfaces/ColorShade.md +1 -1
  58. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  59. package/docs/api/interfaces/DataRecord.md +1 -1
  60. package/docs/api/interfaces/DataTableAction.md +1 -1
  61. package/docs/api/interfaces/DataTableColumn.md +1 -1
  62. package/docs/api/interfaces/DataTableProps.md +1 -1
  63. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  64. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  65. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  66. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  67. package/docs/api/interfaces/FileMetadata.md +1 -1
  68. package/docs/api/interfaces/FileReference.md +1 -1
  69. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  70. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  71. package/docs/api/interfaces/FileUploadProps.md +1 -1
  72. package/docs/api/interfaces/FooterProps.md +1 -1
  73. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  74. package/docs/api/interfaces/InputProps.md +1 -1
  75. package/docs/api/interfaces/LabelProps.md +1 -1
  76. package/docs/api/interfaces/LoginFormProps.md +1 -1
  77. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  78. package/docs/api/interfaces/NavigationContextType.md +1 -1
  79. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  80. package/docs/api/interfaces/NavigationItem.md +1 -1
  81. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  82. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  83. package/docs/api/interfaces/Organisation.md +1 -1
  84. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  85. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  86. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  87. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  88. package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
  89. package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
  90. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  91. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  92. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  93. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  94. package/docs/api/interfaces/PaletteData.md +1 -1
  95. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  96. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  97. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  98. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  99. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  100. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  101. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  102. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  103. package/docs/api/interfaces/RBACConfig.md +19 -8
  104. package/docs/api/interfaces/RBACLogger.md +5 -5
  105. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  106. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  107. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  108. package/docs/api/interfaces/RouteConfig.md +19 -6
  109. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  110. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  111. package/docs/api/interfaces/StorageConfig.md +1 -1
  112. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  113. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  114. package/docs/api/interfaces/StorageListOptions.md +1 -1
  115. package/docs/api/interfaces/StorageListResult.md +1 -1
  116. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  117. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  118. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  119. package/docs/api/interfaces/StyleImport.md +1 -1
  120. package/docs/api/interfaces/SwitchProps.md +1 -1
  121. package/docs/api/interfaces/ToastActionElement.md +1 -1
  122. package/docs/api/interfaces/ToastProps.md +1 -1
  123. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  124. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  125. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  126. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  127. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  128. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  129. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  130. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  131. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  132. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  133. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  134. package/docs/api/interfaces/UserEventAccess.md +1 -1
  135. package/docs/api/interfaces/UserMenuProps.md +1 -1
  136. package/docs/api/interfaces/UserProfile.md +1 -1
  137. package/docs/api/modules.md +44 -43
  138. package/docs/api-reference/hooks.md +8 -4
  139. package/docs/architecture/rpc-function-standards.md +3 -1
  140. package/docs/best-practices/common-patterns.md +3 -3
  141. package/docs/best-practices/deployment.md +10 -4
  142. package/docs/best-practices/performance.md +11 -3
  143. package/docs/core-concepts/organisations.md +8 -8
  144. package/docs/core-concepts/permissions.md +133 -72
  145. package/docs/documentation-index.md +0 -2
  146. package/docs/migration/rbac-migration.md +65 -66
  147. package/docs/rbac/README.md +114 -38
  148. package/docs/rbac/advanced-patterns.md +15 -22
  149. package/docs/rbac/api-reference.md +63 -16
  150. package/docs/rbac/examples.md +12 -12
  151. package/docs/rbac/getting-started.md +19 -19
  152. package/docs/rbac/quick-start.md +110 -35
  153. package/docs/rbac/troubleshooting.md +127 -3
  154. package/package.json +1 -1
  155. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +913 -0
  156. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +609 -0
  157. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +434 -0
  158. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +120 -0
  159. package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +519 -0
  160. package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +316 -0
  161. package/src/components/DataTable/examples/__tests__/InitialPageSizeExample.test.tsx +211 -0
  162. package/src/components/FileUpload/FileUpload.tsx +2 -8
  163. package/src/components/NavigationMenu/NavigationMenu.test.tsx +38 -4
  164. package/src/components/NavigationMenu/NavigationMenu.tsx +71 -6
  165. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +193 -63
  166. package/src/components/PaceAppLayout/PaceAppLayout.tsx +102 -135
  167. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +41 -2
  168. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +61 -6
  169. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +71 -21
  170. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +113 -41
  171. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +155 -45
  172. package/src/components/PaceLoginPage/PaceLoginPage.tsx +30 -1
  173. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +63 -5
  174. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +156 -72
  175. package/src/hooks/__tests__/useRBAC.unit.test.ts +4 -38
  176. package/src/hooks/index.ts +1 -1
  177. package/src/hooks/useFileDisplay.ts +51 -0
  178. package/src/hooks/usePermissionCache.test.ts +112 -68
  179. package/src/hooks/usePermissionCache.ts +55 -15
  180. package/src/rbac/README.md +81 -39
  181. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +3 -3
  182. package/src/rbac/__tests__/engine.comprehensive.test.ts +15 -6
  183. package/src/rbac/__tests__/rbac-core.test.tsx +1 -1
  184. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +57 -4
  185. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +3 -2
  186. package/src/rbac/adapters.tsx +4 -4
  187. package/src/rbac/api.test.ts +39 -15
  188. package/src/rbac/api.ts +27 -9
  189. package/src/rbac/audit.test.ts +2 -2
  190. package/src/rbac/audit.ts +14 -5
  191. package/src/rbac/cache.test.ts +12 -0
  192. package/src/rbac/cache.ts +29 -9
  193. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +1 -1
  194. package/src/rbac/components/NavigationGuard.tsx +14 -14
  195. package/src/rbac/components/NavigationProvider.test.tsx +1 -1
  196. package/src/rbac/components/PagePermissionGuard.tsx +22 -38
  197. package/src/rbac/components/PagePermissionProvider.test.tsx +1 -1
  198. package/src/rbac/components/PermissionEnforcer.tsx +19 -15
  199. package/src/rbac/components/RoleBasedRouter.tsx +16 -9
  200. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +123 -107
  201. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
  202. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +121 -103
  203. package/src/rbac/config.ts +2 -0
  204. package/src/rbac/docs/event-based-apps.md +6 -6
  205. package/src/rbac/engine.ts +27 -7
  206. package/src/rbac/hooks/useCan.test.ts +29 -2
  207. package/src/rbac/hooks/usePermissions.test.ts +25 -25
  208. package/src/rbac/hooks/usePermissions.ts +47 -23
  209. package/src/rbac/hooks/useRBAC.simple.test.ts +1 -8
  210. package/src/rbac/hooks/useRBAC.test.ts +3 -40
  211. package/src/rbac/hooks/useRBAC.ts +0 -55
  212. package/src/rbac/hooks/useResolvedScope.ts +23 -31
  213. package/src/rbac/permissions.test.ts +11 -7
  214. package/src/rbac/security.test.ts +2 -2
  215. package/src/rbac/security.ts +23 -8
  216. package/src/rbac/types.test.ts +2 -2
  217. package/src/rbac/types.ts +1 -2
  218. package/src/services/EventService.ts +41 -13
  219. package/src/services/__tests__/EventService.test.ts +25 -4
  220. package/src/services/interfaces/IEventService.ts +1 -0
  221. package/src/utils/file-reference.ts +9 -0
  222. package/dist/chunk-2W4WKJVF.js.map +0 -1
  223. package/dist/chunk-3TKTL5AZ.js.map +0 -1
  224. package/dist/chunk-AUXS7XSO.js.map +0 -1
  225. package/dist/chunk-F6TSYCKP.js.map +0 -1
  226. package/dist/chunk-P72NKAT5.js.map +0 -1
  227. package/dist/chunk-Q7APDV6H.js.map +0 -1
  228. package/dist/chunk-WWNOVFDC.js.map +0 -1
  229. package/docs/rbac/breaking-changes-v3.md +0 -222
  230. package/docs/rbac/migration-guide.md +0 -260
  231. /package/dist/{DataTable-5HITILXS.js.map → DataTable-5W2HVLLV.js.map} +0 -0
  232. /package/dist/{UnifiedAuthProvider-A7I23UCN.js.map → UnifiedAuthProvider-LUM3QLS5.js.map} +0 -0
  233. /package/dist/{api-5I3E47G2.js.map → api-SIZPFBFX.js.map} +0 -0
  234. /package/dist/{audit-65VNHEV2.js.map → audit-5JI5T3SL.js.map} +0 -0
  235. /package/dist/{chunk-S4D3Z723.js.map → chunk-ACYQNYHB.js.map} +0 -0
  236. /package/dist/{chunk-D6MEKC27.js.map → chunk-EFVQBYFN.js.map} +0 -0
  237. /package/dist/{chunk-EZ64QG2I.js.map → chunk-I5YM5BGS.js.map} +0 -0
  238. /package/dist/{chunk-YFMENCR4.js.map → chunk-JE2GFA3O.js.map} +0 -0
  239. /package/dist/{chunk-UW2DE6JX.js.map → chunk-TD4BXGPE.js.map} +0 -0
  240. /package/dist/{chunk-EYSXQ756.js.map → chunk-TDFBX7KJ.js.map} +0 -0
@@ -25,9 +25,11 @@ A simple user management app that demonstrates:
25
25
 
26
26
  1. **ALWAYS call `setupRBAC()` first** - This is MANDATORY and must be called before any RBAC features
27
27
  2. **Never make direct database queries** to `rbac_apps`, `rbac_global_roles`, or other RBAC tables
28
- 3. **Always use `PagePermissionGuard`** for page-level permissions (not manual permission checks)
29
- 4. **Always set up providers correctly** in the exact order shown
30
- 5. **Use the exact app name** from your environment variable (must match database exactly)
28
+ 3. **ALWAYS use `PagePermissionGuard`** for page-level permissions - This is the ONLY way to properly protect pages
29
+ 4. **ALWAYS set up providers correctly** in the exact order shown below
30
+ 5. **Use the exact app name** from your environment variable (must match database exactly, case-sensitive)
31
+ 6. **Ensure database setup is complete** before starting the app - App must be registered with pages and permissions
32
+ 7. **User must have organisation role** - Users need roles assigned in `rbac_organisation_roles` table
31
33
 
32
34
  ## 🚀 Step-by-Step Implementation
33
35
 
@@ -127,8 +129,11 @@ WHERE a.name = 'user-manager';
127
129
 
128
130
  #### 5.3 Set Up Page Permissions
129
131
 
132
+ **CRITICAL**: Page permissions use the format `{operation}:page.{pageName}` (e.g., `read:page.dashboard`). The database stores permissions per page per role per organisation.
133
+
130
134
  ```sql
131
135
  -- Set up basic permissions for each page (replace 'user-manager' with your actual app name)
136
+ -- IMPORTANT: Replace the organisation_id UUID with your actual organisation ID
132
137
  WITH app_pages AS (
133
138
  SELECT ap.id as page_id, ap.page_name, a.id as app_id
134
139
  FROM rbac_app_pages ap
@@ -146,10 +151,24 @@ SELECT
146
151
  WHEN role.role_name = 'member' AND ap.page_name IN ('dashboard', 'users') THEN true
147
152
  ELSE false
148
153
  END,
149
- '00000000-0000-0000-0000-000000000000'::uuid -- Replace with your organisation ID
154
+ '00000000-0000-0000-0000-000000000000'::uuid -- ⚠️ REPLACE THIS with your actual organisation ID
150
155
  FROM app_pages ap
151
156
  CROSS JOIN (SELECT unnest(ARRAY['read', 'create', 'update', 'delete']) as operation) op
152
157
  CROSS JOIN (SELECT unnest(ARRAY['org_admin', 'leader', 'member']) as role_name) role;
158
+
159
+ -- Verify permissions were created correctly
160
+ SELECT
161
+ a.name as app_name,
162
+ ap.page_name,
163
+ pp.operation,
164
+ pp.role_name,
165
+ pp.allowed,
166
+ pp.organisation_id
167
+ FROM rbac_page_permissions pp
168
+ JOIN rbac_app_pages ap ON pp.app_page_id = ap.id
169
+ JOIN rbac_apps a ON ap.app_id = a.id
170
+ WHERE a.name = 'user-manager'
171
+ ORDER BY ap.page_name, pp.operation, pp.role_name;
153
172
  ```
154
173
 
155
174
  #### 5.4 Assign User Roles
@@ -202,28 +221,23 @@ if (!supabaseUrl || !supabaseAnonKey) {
202
221
  export const supabase = createClient(supabaseUrl, supabaseAnonKey)
203
222
  ```
204
223
 
205
- ### 8. Initialize RBAC (MANDATORY - NEW STEP!)
224
+ ### 8. Initialize RBAC (MANDATORY!)
206
225
 
207
- **CRITICAL**: You MUST call `setupRBAC()` before using any RBAC features. Create `src/lib/rbac-setup.ts`:
226
+ **CRITICAL**: You MUST call `setupRBAC()` before using any RBAC features. This configures rate limiting (default: 1000 requests/minute) and enables security validation.
208
227
 
209
- ```typescript
210
- // src/lib/rbac-setup.ts
211
- import { setupRBAC } from '@jmruthers/pace-core/rbac'
212
- import { supabase } from './supabase'
213
-
214
- // ⚠️ REQUIRED: Initialize RBAC before using any RBAC features
215
- setupRBAC(supabase)
216
- ```
217
-
218
- **Then import this in your main entry point:**
228
+ **Option 1: Setup in main entry point (Recommended)**
219
229
 
220
230
  ```typescript
221
231
  // src/main.tsx
222
232
  import React from 'react'
223
233
  import ReactDOM from 'react-dom/client'
224
- import './lib/rbac-setup' // ⚠️ Import setup BEFORE App
234
+ import { setupRBAC } from '@jmruthers/pace-core/rbac'
235
+ import { supabase } from './lib/supabase'
225
236
  import App from './App'
226
237
 
238
+ // ⚠️ CRITICAL: Call setupRBAC BEFORE rendering App
239
+ setupRBAC(supabase)
240
+
227
241
  ReactDOM.createRoot(document.getElementById('root')!).render(
228
242
  <React.StrictMode>
229
243
  <App />
@@ -231,18 +245,24 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
231
245
  )
232
246
  ```
233
247
 
234
- **Or in your App.tsx (alternative approach):**
248
+ **Option 2: Setup in separate file (Alternative)**
235
249
 
236
250
  ```typescript
237
- // src/App.tsx
238
- import React from 'react'
251
+ // src/lib/rbac-setup.ts
239
252
  import { setupRBAC } from '@jmruthers/pace-core/rbac'
240
- import { supabase } from './lib/supabase'
253
+ import { supabase } from './supabase'
241
254
 
242
- // ⚠️ REQUIRED: Call setupRBAC before anything else
243
- setupRBAC(supabase)
255
+ // ⚠️ REQUIRED: Initialize RBAC before using any RBAC features
256
+ setupRBAC(supabase, {
257
+ // Optional: Configure rate limiting for high-traffic apps
258
+ security: {
259
+ maxPermissionChecksPerMinute: 2000 // Increase from default 1000 if needed
260
+ }
261
+ })
244
262
 
245
- // ... rest of your app
263
+ // src/main.tsx
264
+ import './lib/rbac-setup' // Import BEFORE App
265
+ import App from './App'
246
266
  ```
247
267
 
248
268
  ### 9. App Setup (CRITICAL)
@@ -560,12 +580,25 @@ export function Users() {
560
580
 
561
581
  Your RBAC setup is working correctly if:
562
582
 
583
+ **Setup Checklist:**
584
+ - [ ] `setupRBAC(supabase)` is called before rendering App
585
+ - [ ] App is registered in `rbac_apps` table with `is_active = true`
586
+ - [ ] Pages exist in `rbac_app_pages` for your app
587
+ - [ ] Page permissions exist in `rbac_page_permissions` for your pages, roles, and organisation
588
+ - [ ] User has an active role in `rbac_organisation_roles` for the organisation
589
+ - [ ] `VITE_APP_NAME` environment variable matches database `rbac_apps.name` exactly
590
+
591
+ **Runtime Checklist:**
563
592
  - [ ] You can log in successfully
593
+ - [ ] Browser console shows "RBAC system initialized successfully"
564
594
  - [ ] You see the dashboard without "Access Denied" messages
595
+ - [ ] `PagePermissionGuard` shows loading state, then renders content (not fallback)
565
596
  - [ ] You can navigate to the users page
566
597
  - [ ] You see "Success! Your RBAC setup is working correctly" on the users page
567
598
  - [ ] No 400/406 errors in the browser console
568
599
  - [ ] No "App not found" errors in the console
600
+ - [ ] No "STRICT MODE VIOLATION" errors in the console
601
+ - [ ] No "rate_limit_exceeded" errors (if you see these, increase rate limit in setupRBAC)
569
602
 
570
603
  ## 🚨 Troubleshooting
571
604
 
@@ -579,33 +612,75 @@ import { setupRBAC } from '@jmruthers/pace-core/rbac'
579
612
  setupRBAC(supabase) // Must be called BEFORE rendering app
580
613
  ```
581
614
 
582
- ### Issue: "Access Denied" on all pages
615
+ ### Issue: "Access Denied" or "STRICT MODE VIOLATION" on all pages
616
+
617
+ **This is the most common issue. Check these in EXACT ORDER:**
583
618
 
584
- **Check these in order:**
619
+ 1. **Verify setupRBAC was called**:
620
+ - Check browser console for "RBAC system initialized successfully"
621
+ - If missing, add `setupRBAC(supabase)` in your main.tsx before rendering
585
622
 
586
- 1. **Verify your app is registered**:
623
+ 2. **Verify your app is registered and active**:
587
624
  ```sql
588
- SELECT * FROM rbac_apps WHERE name = 'user-manager';
625
+ SELECT id, name, display_name, requires_event, is_active
626
+ FROM rbac_apps
627
+ WHERE name = 'user-manager';
589
628
  ```
629
+ - Must return exactly 1 row
630
+ - `is_active` must be `true`
631
+ - `name` must match your `VITE_APP_NAME` exactly (case-sensitive)
590
632
 
591
- 2. **Check your environment variable**:
633
+ 3. **Check your environment variable matches database**:
592
634
  ```typescript
593
- console.log('App name:', import.meta.env.VITE_APP_NAME);
635
+ console.log('App name from env:', import.meta.env.VITE_APP_NAME);
636
+ ```
637
+ - Must match database `rbac_apps.name` exactly
638
+
639
+ 4. **Verify pages exist for your app**:
640
+ ```sql
641
+ SELECT ap.id, ap.page_name, ap.page_description
642
+ FROM rbac_app_pages ap
643
+ JOIN rbac_apps a ON ap.app_id = a.id
644
+ WHERE a.name = 'user-manager'
645
+ ORDER BY ap.page_name;
594
646
  ```
647
+ - Must have pages for `dashboard`, `users`, etc.
595
648
 
596
- 3. **Verify user has roles**:
649
+ 5. **Verify user has organisation role**:
597
650
  ```sql
598
- SELECT * FROM rbac_organisation_roles WHERE user_id = 'your-user-id';
651
+ SELECT ror.*, u.email
652
+ FROM rbac_organisation_roles ror
653
+ JOIN auth.users u ON ror.user_id = u.id
654
+ WHERE ror.user_id = 'your-user-id'::uuid
655
+ AND ror.status = 'active';
599
656
  ```
657
+ - User must have at least one active role
658
+ - Must be for the organisation you're testing with
600
659
 
601
- 4. **Check page permissions exist**:
660
+ 6. **Check page permissions exist for user's role**:
602
661
  ```sql
603
- SELECT pp.*, ap.page_name, a.name as app_name
662
+ SELECT
663
+ ap.page_name,
664
+ pp.operation,
665
+ pp.role_name,
666
+ pp.allowed,
667
+ pp.organisation_id
604
668
  FROM rbac_page_permissions pp
605
669
  JOIN rbac_app_pages ap ON pp.app_page_id = ap.id
606
670
  JOIN rbac_apps a ON ap.app_id = a.id
607
- WHERE a.name = 'user-manager';
671
+ WHERE a.name = 'user-manager'
672
+ AND pp.role_name = 'org_admin' -- Replace with your user's role
673
+ AND pp.organisation_id = 'your-org-id'::uuid -- Replace with your org ID
674
+ AND pp.allowed = true;
608
675
  ```
676
+ - Must have permissions for the pages you're trying to access
677
+ - Must have `allowed = true` for the operation (read, create, etc.)
678
+ - `organisation_id` must match the organisation the user is accessing
679
+
680
+ 7. **Check browser console for specific errors**:
681
+ - Look for `[PagePermissionGuard]` error messages
682
+ - Look for `[useCan]` permission check logs
683
+ - Look for rate limit errors (if you see these, you may need to increase the limit)
609
684
 
610
685
  ### Issue: 400/406 Database Errors
611
686
 
@@ -12,11 +12,134 @@ This guide helps you resolve common issues when using the PACE Core RBAC system.
12
12
 
13
13
  ## 🚨 Critical Issues (Fix Immediately)
14
14
 
15
- ### 1. "Access Denied" on All Pages - Missing App ID
15
+ ### 1. "STRICT MODE VIOLATION" Error - Page Access Denied
16
+
17
+ **Symptoms:**
18
+ - Console shows `[PagePermissionGuard] STRICT MODE VIOLATION: User attempted to access protected page without permission`
19
+ - Error object shows: `{ pageName: "menus", operation: "read", userId: "...", scope: {...} }`
20
+ - Pages show fallback/access denied content even for users who should have permission
21
+ - All permission checks return `false`
22
+
23
+ **Root Cause:** One or more of these issues:
24
+ 1. Missing or incorrect database setup (app, pages, or permissions)
25
+ 2. User doesn't have organisation role for the organisation being accessed
26
+ 3. Page permissions don't exist for user's role and organisation
27
+ 4. `setupRBAC()` wasn't called before using RBAC features
28
+ 5. Rate limiting is too restrictive (unlikely with default 1000/minute)
29
+
30
+ **Fix (Check in order):**
31
+
32
+ **Step 1: Verify setupRBAC was called**
33
+ ```typescript
34
+ // Check browser console for this message:
35
+ // "RBAC system initialized successfully"
36
+
37
+ // If missing, add to main.tsx:
38
+ import { setupRBAC } from '@jmruthers/pace-core/rbac';
39
+ setupRBAC(supabase); // BEFORE rendering App
40
+ ```
41
+
42
+ **Step 2: Verify app registration**
43
+ ```sql
44
+ -- Check app exists and is active
45
+ SELECT id, name, display_name, requires_event, is_active
46
+ FROM rbac_apps
47
+ WHERE name = 'your-app-name'; -- Replace with actual app name
48
+
49
+ -- Must return 1 row with is_active = true
50
+ -- name must match VITE_APP_NAME environment variable exactly
51
+ ```
52
+
53
+ **Step 3: Verify pages exist**
54
+ ```sql
55
+ -- Check pages exist for your app
56
+ SELECT ap.id, ap.page_name, ap.page_description
57
+ FROM rbac_app_pages ap
58
+ JOIN rbac_apps a ON ap.app_id = a.id
59
+ WHERE a.name = 'your-app-name'
60
+ ORDER BY ap.page_name;
61
+
62
+ -- Must have a page matching your pageName prop (e.g., "menus")
63
+ ```
64
+
65
+ **Step 4: Verify user has organisation role**
66
+ ```sql
67
+ -- Check user's role for the organisation
68
+ SELECT ror.*, u.email
69
+ FROM rbac_organisation_roles ror
70
+ JOIN auth.users u ON ror.user_id = u.id
71
+ WHERE ror.user_id = 'user-id-from-error'::uuid
72
+ AND ror.organisation_id = 'org-id-from-scope'::uuid -- From error scope
73
+ AND ror.status = 'active';
74
+
75
+ -- User must have at least one active role (e.g., 'org_admin', 'leader', 'member')
76
+ ```
77
+
78
+ **Step 5: Verify page permissions exist**
79
+ ```sql
80
+ -- Check permissions for user's role, page, and organisation
81
+ SELECT
82
+ ap.page_name,
83
+ pp.operation,
84
+ pp.role_name,
85
+ pp.allowed,
86
+ pp.organisation_id
87
+ FROM rbac_page_permissions pp
88
+ JOIN rbac_app_pages ap ON pp.app_page_id = ap.id
89
+ JOIN rbac_apps a ON ap.app_id = a.id
90
+ WHERE a.name = 'your-app-name'
91
+ AND ap.page_name = 'menus' -- From error pageName
92
+ AND pp.operation = 'read' -- From error operation
93
+ AND pp.role_name = 'org_admin' -- From Step 4 role
94
+ AND pp.organisation_id = 'org-id-from-scope'::uuid -- From error scope
95
+ AND pp.allowed = true;
96
+
97
+ -- Must return at least 1 row with allowed = true
98
+ ```
99
+
100
+ **Step 6: Create missing permissions if needed**
101
+ ```sql
102
+ -- Grant read permission for a page to a role
103
+ INSERT INTO rbac_page_permissions (app_page_id, operation, role_name, allowed, organisation_id)
104
+ SELECT
105
+ ap.id,
106
+ 'read',
107
+ 'org_admin', -- Or user's actual role
108
+ true,
109
+ 'your-org-id'::uuid -- From error scope
110
+ FROM rbac_app_pages ap
111
+ JOIN rbac_apps a ON ap.app_id = a.id
112
+ WHERE a.name = 'your-app-name'
113
+ AND ap.page_name = 'menus';
114
+ ```
115
+
116
+ ### 2. "rate_limit_exceeded" Error
117
+
118
+ **Symptoms:**
119
+ - Console shows `[RBAC Security] Object { type: "rate_limit_exceeded", userId: "...", details: {...} }`
120
+ - Permission checks stop working temporarily
121
+ - Users can't access pages they should have permission for
122
+
123
+ **Root Cause:** Default rate limit (1000 requests/minute) exceeded
124
+
125
+ **Fix:**
126
+ ```typescript
127
+ // Increase rate limit in setupRBAC
128
+ import { setupRBAC } from '@jmruthers/pace-core/rbac';
129
+
130
+ setupRBAC(supabase, {
131
+ security: {
132
+ maxPermissionChecksPerMinute: 2000 // Increase from default 1000
133
+ }
134
+ });
135
+ ```
136
+
137
+ **Note:** Rate limiting is per user, so if you have many components checking permissions on page load, you may need a higher limit.
138
+
139
+ ### 3. "Access Denied" on All Pages - Missing App ID
16
140
 
17
141
  **Symptoms:**
18
142
  - Users see "Access Denied" on every page
19
- - Console shows `[PagePermissionGuard] STRICT MODE VIOLATION` errors
20
143
  - Console shows `scope: { organisationId: "...", eventId: "..." }` (missing `appId`)
21
144
  - All permission checks return `false`
22
145
 
@@ -62,7 +185,8 @@ function App() {
62
185
  **Fix:**
63
186
  ```typescript
64
187
  // ❌ WRONG - This causes the issue
65
- const { hasPermission } = useRBAC();
188
+ // Don't use useRBAC().hasPermission() - it has been removed
189
+ // Use useCan() hook or PagePermissionGuard instead
66
190
 
67
191
  // ✅ CORRECT - Use PagePermissionGuard instead
68
192
  <PagePermissionGuard
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.5.109",
3
+ "version": "0.5.111",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {