@jmruthers/pace-core 0.5.76 → 0.5.78

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 (447) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-Df3IozMG.d.ts} +10 -118
  3. package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
  4. package/dist/{DataTable-4GAVPIEG.js → DataTable-ETGVF4Y5.js} +50 -13
  5. package/dist/{PublicLoadingSpinner-BiNER8F5.d.ts → PublicLoadingSpinner-CnUaz0vG.d.ts} +5 -2
  6. package/dist/{UnifiedAuthProvider-Bj6YCf7c.d.ts → UnifiedAuthProvider-B391Aqum.d.ts} +42 -45
  7. package/dist/{UnifiedAuthProvider-3NKDOSOK.js → UnifiedAuthProvider-P5SOJAQ6.js} +4 -5
  8. package/dist/{api-DDMUKIUD.js → api-KG4A2X7P.js} +9 -3
  9. package/dist/{audit-6TOCAMKO.js → audit-65VNHEV2.js} +2 -2
  10. package/dist/{chunk-K34IM5CT.js → chunk-2OGV6IRV.js} +196 -626
  11. package/dist/chunk-2OGV6IRV.js.map +1 -0
  12. package/dist/{chunk-NTNILOBC.js → chunk-5BO3MI5Y.js} +4 -4
  13. package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
  14. package/dist/chunk-CVMVPYAL.js.map +1 -0
  15. package/dist/{chunk-URUTVZ7N.js → chunk-FL4ZCQLD.js} +2 -2
  16. package/dist/{chunk-LW7MMEAQ.js → chunk-FT2M4R4F.js} +2 -2
  17. package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
  18. package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
  19. package/dist/{chunk-KHJS6VIA.js → chunk-LRQ6RBJC.js} +157 -112
  20. package/dist/chunk-LRQ6RBJC.js.map +1 -0
  21. package/dist/{chunk-WN6XJWOS.js → chunk-MNJXXD6C.js} +274 -743
  22. package/dist/chunk-MNJXXD6C.js.map +1 -0
  23. package/dist/{chunk-KK73ZB4E.js → chunk-PTR5PMPE.js} +153 -132
  24. package/dist/chunk-PTR5PMPE.js.map +1 -0
  25. package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
  26. package/dist/chunk-Q7APDV6H.js.map +1 -0
  27. package/dist/{chunk-A4FUBC7B.js → chunk-QGVSOUJ2.js} +2 -4
  28. package/dist/{chunk-A4FUBC7B.js.map → chunk-QGVSOUJ2.js.map} +1 -1
  29. package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
  30. package/dist/chunk-S63MFSY6.js.map +1 -0
  31. package/dist/{chunk-AFGTSUAD.js → chunk-VSOKOFRF.js} +4 -4
  32. package/dist/chunk-WUXCWRL6.js +20 -0
  33. package/dist/chunk-WUXCWRL6.js.map +1 -0
  34. package/dist/{chunk-Y6TXWPJO.js → chunk-YVVGHRGI.js} +105 -31
  35. package/dist/chunk-YVVGHRGI.js.map +1 -0
  36. package/dist/{chunk-M5IWZRBT.js → chunk-ZMNXIJP4.js} +2187 -981
  37. package/dist/chunk-ZMNXIJP4.js.map +1 -0
  38. package/dist/components.d.ts +6 -6
  39. package/dist/components.js +14 -18
  40. package/dist/components.js.map +1 -1
  41. package/dist/{database-C3Szpi5J.d.ts → database-BXAfr2Y_.d.ts} +18 -0
  42. package/dist/hooks.d.ts +5 -5
  43. package/dist/hooks.js +8 -9
  44. package/dist/hooks.js.map +1 -1
  45. package/dist/index.d.ts +19 -27
  46. package/dist/index.js +21 -29
  47. package/dist/index.js.map +1 -1
  48. package/dist/{organisation-BtshODVF.d.ts → organisation-D6qRDtbF.d.ts} +1 -1
  49. package/dist/providers.d.ts +7 -21
  50. package/dist/providers.js +3 -10
  51. package/dist/rbac/index.d.ts +71 -221
  52. package/dist/rbac/index.js +15 -16
  53. package/dist/{types-CGX9Vyf5.d.ts → types-BDg1mAGG.d.ts} +36 -6
  54. package/dist/types.d.ts +3 -3
  55. package/dist/types.js +61 -18
  56. package/dist/types.js.map +1 -1
  57. package/dist/{unified-CM7T0aTK.d.ts → unified-DQ4VcT7H.d.ts} +1 -1
  58. package/dist/{usePublicRouteParams-B-CumWRc.d.ts → usePublicRouteParams-BlgwXweB.d.ts} +3 -3
  59. package/dist/utils.d.ts +2 -2
  60. package/dist/utils.js +52 -9
  61. package/dist/utils.js.map +1 -1
  62. package/docs/CONTENT_AUDIT_REPORT.md +253 -0
  63. package/docs/DOCUMENTATION_AUDIT.md +172 -0
  64. package/docs/README.md +142 -147
  65. package/docs/STYLE_GUIDE.md +37 -0
  66. package/docs/api/classes/ColumnFactory.md +17 -17
  67. package/docs/api/classes/ErrorBoundary.md +1 -1
  68. package/docs/api/classes/InvalidScopeError.md +4 -4
  69. package/docs/api/classes/MissingUserContextError.md +4 -4
  70. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  71. package/docs/api/classes/PermissionDeniedError.md +5 -5
  72. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  73. package/docs/api/classes/RBACAuditManager.md +8 -8
  74. package/docs/api/classes/RBACCache.md +35 -5
  75. package/docs/api/classes/RBACEngine.md +49 -20
  76. package/docs/api/classes/RBACError.md +4 -4
  77. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  78. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  79. package/docs/api/classes/StorageUtils.md +1 -1
  80. package/docs/api/enums/FileCategory.md +1 -1
  81. package/docs/api/interfaces/AggregateConfig.md +4 -4
  82. package/docs/api/interfaces/ButtonProps.md +1 -1
  83. package/docs/api/interfaces/CardProps.md +1 -1
  84. package/docs/api/interfaces/ColorPalette.md +1 -1
  85. package/docs/api/interfaces/ColorShade.md +1 -1
  86. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  87. package/docs/api/interfaces/DataRecord.md +11 -0
  88. package/docs/api/interfaces/DataTableAction.md +65 -29
  89. package/docs/api/interfaces/DataTableColumn.md +36 -23
  90. package/docs/api/interfaces/DataTableProps.md +80 -38
  91. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  92. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  93. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  94. package/docs/api/interfaces/EventLogoProps.md +1 -1
  95. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  96. package/docs/api/interfaces/FileMetadata.md +1 -1
  97. package/docs/api/interfaces/FileReference.md +1 -1
  98. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  99. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  100. package/docs/api/interfaces/FileUploadProps.md +1 -1
  101. package/docs/api/interfaces/FooterProps.md +1 -1
  102. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  103. package/docs/api/interfaces/InputProps.md +1 -1
  104. package/docs/api/interfaces/LabelProps.md +1 -1
  105. package/docs/api/interfaces/LoginFormProps.md +1 -1
  106. package/docs/api/interfaces/NavigationAccessRecord.md +11 -11
  107. package/docs/api/interfaces/NavigationContextType.md +9 -9
  108. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  109. package/docs/api/interfaces/NavigationItem.md +1 -1
  110. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  111. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  112. package/docs/api/interfaces/Organisation.md +1 -1
  113. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  114. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  115. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  116. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  117. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  118. package/docs/api/interfaces/PaceLoginPageProps.md +16 -3
  119. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  120. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  121. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  122. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  123. package/docs/api/interfaces/PaletteData.md +1 -1
  124. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  125. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  126. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  127. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  128. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  129. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  130. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  131. package/docs/api/interfaces/RBACConfig.md +1 -1
  132. package/docs/api/interfaces/RBACLogger.md +1 -1
  133. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  134. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  135. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  136. package/docs/api/interfaces/RouteConfig.md +2 -2
  137. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  138. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  139. package/docs/api/interfaces/StorageConfig.md +1 -1
  140. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  141. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  142. package/docs/api/interfaces/StorageListOptions.md +1 -1
  143. package/docs/api/interfaces/StorageListResult.md +1 -1
  144. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  145. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  146. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  147. package/docs/api/interfaces/StyleImport.md +1 -1
  148. package/docs/api/interfaces/SwitchProps.md +1 -1
  149. package/docs/api/interfaces/ToastActionElement.md +1 -1
  150. package/docs/api/interfaces/ToastProps.md +1 -1
  151. package/docs/api/interfaces/UnifiedAuthContextType.md +94 -521
  152. package/docs/api/interfaces/UnifiedAuthProviderProps.md +16 -16
  153. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  154. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  155. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  156. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  157. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  158. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  159. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  160. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  161. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  162. package/docs/api/interfaces/UserEventAccess.md +11 -11
  163. package/docs/api/interfaces/UserMenuProps.md +1 -1
  164. package/docs/api/interfaces/UserProfile.md +1 -1
  165. package/docs/api/modules.md +251 -269
  166. package/docs/api-reference/components.md +193 -0
  167. package/docs/api-reference/hooks.md +265 -0
  168. package/docs/api-reference/providers.md +6 -0
  169. package/docs/api-reference/types.md +6 -0
  170. package/docs/api-reference/utilities.md +207 -0
  171. package/docs/architecture/README.md +6 -0
  172. package/docs/{database-schema-requirements.md → architecture/database-schema-requirements.md} +6 -0
  173. package/docs/architecture/rbac-security-architecture.md +258 -0
  174. package/docs/architecture/services.md +9 -1
  175. package/docs/best-practices/README.md +6 -0
  176. package/docs/best-practices/accessibility.md +6 -0
  177. package/docs/{common-patterns.md → best-practices/common-patterns.md} +6 -0
  178. package/docs/best-practices/deployment.md +6 -0
  179. package/docs/best-practices/performance.md +475 -2
  180. package/docs/best-practices/security.md +6 -0
  181. package/docs/best-practices/testing.md +6 -0
  182. package/docs/core-concepts/authentication.md +6 -0
  183. package/docs/core-concepts/events.md +6 -0
  184. package/docs/core-concepts/organisations.md +6 -0
  185. package/docs/core-concepts/permissions.md +6 -0
  186. package/docs/core-concepts/rbac-system.md +8 -0
  187. package/docs/documentation-index.md +121 -182
  188. package/docs/{consuming-app-vite-config.md → getting-started/consuming-app-vite-config.md} +6 -0
  189. package/docs/getting-started/documentation-index.md +40 -0
  190. package/docs/getting-started/examples/README.md +878 -35
  191. package/docs/{faq.md → getting-started/faq.md} +7 -1
  192. package/docs/getting-started/installation-guide.md +6 -0
  193. package/docs/{quick-reference.md → getting-started/quick-reference.md} +6 -0
  194. package/docs/implementation-guides/app-layout.md +6 -0
  195. package/docs/implementation-guides/authentication.md +1021 -0
  196. package/docs/implementation-guides/component-styling.md +6 -0
  197. package/docs/implementation-guides/data-tables.md +1264 -2076
  198. package/docs/implementation-guides/dynamic-colors.md +6 -0
  199. package/docs/implementation-guides/event-theming-summary.md +6 -0
  200. package/docs/{file-reference-system.md → implementation-guides/file-reference-system.md} +6 -0
  201. package/docs/implementation-guides/file-upload-storage.md +6 -0
  202. package/docs/implementation-guides/forms.md +6 -0
  203. package/docs/implementation-guides/inactivity-tracking.md +6 -0
  204. package/docs/implementation-guides/navigation.md +6 -0
  205. package/docs/implementation-guides/organisation-security.md +6 -0
  206. package/docs/implementation-guides/permission-enforcement.md +6 -0
  207. package/docs/implementation-guides/public-pages-advanced.md +6 -0
  208. package/docs/implementation-guides/public-pages.md +6 -0
  209. package/docs/migration/MIGRATION_GUIDE.md +827 -351
  210. package/docs/migration/README.md +7 -1
  211. package/docs/migration/organisation-context-timing-fix.md +6 -0
  212. package/docs/migration/rbac-migration.md +44 -1
  213. package/docs/migration/service-architecture.md +6 -0
  214. package/docs/migration/v0.4.15-tailwind-scanning.md +6 -0
  215. package/docs/migration/v0.4.16-css-first-approach.md +6 -0
  216. package/docs/migration/v0.4.17-source-path-fix.md +6 -0
  217. package/docs/rbac/README-rbac-rls-integration.md +6 -0
  218. package/docs/rbac/README.md +6 -0
  219. package/docs/rbac/advanced-patterns.md +6 -0
  220. package/docs/rbac/api-reference.md +7 -1
  221. package/docs/rbac/breaking-changes-v3.md +222 -0
  222. package/docs/rbac/examples/rbac-rls-integration-example.md +6 -0
  223. package/docs/rbac/examples.md +6 -0
  224. package/docs/rbac/getting-started.md +6 -0
  225. package/docs/rbac/migration-guide.md +260 -0
  226. package/docs/rbac/quick-start.md +70 -13
  227. package/docs/rbac/rbac-rls-integration.md +6 -0
  228. package/docs/rbac/super-admin-guide.md +6 -0
  229. package/docs/rbac/troubleshooting.md +6 -0
  230. package/docs/security/README.md +6 -0
  231. package/docs/security/checklist.md +6 -0
  232. package/docs/styles/README.md +7 -1
  233. package/docs/{usage.md → styles/usage.md} +6 -0
  234. package/docs/testing/README.md +6 -0
  235. package/docs/{visual-testing.md → testing/visual-testing.md} +6 -0
  236. package/docs/troubleshooting/README.md +387 -5
  237. package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +6 -0
  238. package/docs/troubleshooting/common-issues.md +6 -0
  239. package/docs/troubleshooting/database-view-compatibility.md +6 -0
  240. package/docs/troubleshooting/organisation-context-setup.md +6 -0
  241. package/docs/troubleshooting/react-hooks-issue-analysis.md +6 -0
  242. package/docs/troubleshooting/styling-issues.md +6 -0
  243. package/docs/troubleshooting/tailwind-content-scanning.md +6 -0
  244. package/package.json +1 -1
  245. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -1
  246. package/src/__tests__/helpers/test-providers.tsx +3 -53
  247. package/src/components/DataTable/DataTable.test.tsx +319 -0
  248. package/src/components/DataTable/DataTable.tsx +32 -11
  249. package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx.skip} +6 -4
  250. package/src/components/DataTable/__tests__/{DataTable.test.tsx → DataTable.test.tsx.skip} +6 -4
  251. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +31 -9
  252. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +601 -0
  253. package/src/components/DataTable/__tests__/keyboard.test.tsx +615 -0
  254. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +639 -0
  255. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx.skip +330 -0
  256. package/src/components/DataTable/components/AccessDeniedPage.tsx +2 -2
  257. package/src/components/DataTable/components/ActionButtons.tsx +88 -104
  258. package/src/components/DataTable/components/DataTableCore.tsx +309 -337
  259. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +4 -2
  260. package/src/components/DataTable/components/DataTableModals.tsx +22 -1
  261. package/src/components/DataTable/components/EditableRow.tsx +69 -84
  262. package/src/components/DataTable/components/EmptyState.tsx +5 -1
  263. package/src/components/DataTable/components/ImportModal.tsx +65 -36
  264. package/src/components/DataTable/components/PaginationControls.tsx +40 -100
  265. package/src/components/DataTable/components/UnifiedTableBody.tsx +125 -148
  266. package/src/components/DataTable/context/DataTableContext.tsx +1 -1
  267. package/src/components/DataTable/core/ColumnFactory.ts +5 -0
  268. package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +12 -10
  269. package/src/components/DataTable/examples/HierarchicalExample.tsx +1 -1
  270. package/src/components/DataTable/examples/InitialPageSizeExample.tsx +1 -0
  271. package/src/components/DataTable/examples/PerformanceExample.tsx +1 -0
  272. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +1 -5
  273. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +167 -0
  274. package/src/components/DataTable/hooks/index.ts +7 -0
  275. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +32 -15
  276. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +102 -0
  277. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +89 -0
  278. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +117 -0
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +71 -27
  280. package/src/components/DataTable/hooks/useDataTableState.ts +39 -11
  281. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +33 -0
  282. package/src/components/DataTable/hooks/useHierarchicalState.ts +15 -1
  283. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +447 -0
  284. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +94 -0
  285. package/src/components/DataTable/hooks/useTableColumns.ts +10 -7
  286. package/src/components/DataTable/hooks/useTableHandlers.ts +174 -0
  287. package/src/components/DataTable/index.ts +12 -3
  288. package/src/components/DataTable/types.ts +129 -9
  289. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +159 -22
  290. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +111 -0
  291. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +15 -29
  292. package/src/components/DataTable/utils/a11yUtils.ts +244 -0
  293. package/src/components/DataTable/utils/debugTools.ts +609 -0
  294. package/src/components/DataTable/utils/exportUtils.ts +114 -16
  295. package/src/components/DataTable/utils/flexibleImport.ts +202 -32
  296. package/src/components/DataTable/utils/hierarchicalUtils.ts +1 -1
  297. package/src/components/DataTable/utils/index.ts +2 -0
  298. package/src/components/DataTable/utils/paginationUtils.ts +350 -0
  299. package/src/components/DataTable/utils/rowUtils.ts +6 -5
  300. package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -24
  301. package/src/components/NavigationMenu/NavigationMenu.tsx +19 -8
  302. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +1 -23
  303. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +56 -6
  304. package/src/components/PaceLoginPage/PaceLoginPage.tsx +137 -13
  305. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +1 -1
  306. package/src/components/Select/Select.tsx +1 -0
  307. package/src/components/examples/PermissionExample.tsx +173 -0
  308. package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
  309. package/src/examples/PublicEventPage.tsx +274 -0
  310. package/src/examples/PublicPageApp.tsx +308 -0
  311. package/src/examples/PublicPageUsageExample.tsx +216 -0
  312. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +12 -1
  313. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +129 -17
  314. package/src/hooks/__tests__/useRBAC.unit.test.ts +151 -846
  315. package/src/hooks/useOrganisationPermissions.test.ts +42 -18
  316. package/src/hooks/useOrganisationPermissions.ts +12 -6
  317. package/src/hooks/useOrganisationSecurity.test.ts +138 -85
  318. package/src/hooks/useOrganisationSecurity.ts +41 -10
  319. package/src/index.ts +0 -1
  320. package/src/providers/AuthProvider.simplified.tsx +880 -0
  321. package/src/providers/UnifiedAuthProvider.test.simple.tsx +8 -8
  322. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +29 -19
  323. package/src/providers/index.ts +0 -1
  324. package/src/providers/services/EventServiceProvider.tsx +19 -15
  325. package/src/providers/services/InactivityServiceProvider.tsx +19 -15
  326. package/src/providers/services/OrganisationServiceProvider.tsx +19 -15
  327. package/src/providers/services/UnifiedAuthProvider.tsx +156 -127
  328. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +1 -1
  329. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -3
  330. package/src/rbac/README.md +1 -1
  331. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +25 -27
  332. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +313 -0
  333. package/src/rbac/__tests__/engine.comprehensive.test.ts +114 -348
  334. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +28 -110
  335. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +33 -85
  336. package/src/rbac/__tests__/scenarios.user-role.test.tsx +2 -2
  337. package/src/rbac/adapters.tsx +26 -69
  338. package/src/rbac/api.test.ts +90 -27
  339. package/src/rbac/api.ts +61 -10
  340. package/src/rbac/audit.test.ts +33 -38
  341. package/src/rbac/audit.ts +21 -6
  342. package/src/rbac/cache.ts +33 -1
  343. package/src/rbac/components/NavigationGuard.tsx +11 -11
  344. package/src/rbac/components/NavigationProvider.test.tsx +11 -5
  345. package/src/rbac/components/NavigationProvider.tsx +37 -13
  346. package/src/rbac/components/PagePermissionGuard.tsx +111 -50
  347. package/src/rbac/components/PagePermissionProvider.tsx +5 -5
  348. package/src/rbac/components/PermissionEnforcer.tsx +11 -11
  349. package/src/rbac/components/RoleBasedRouter.tsx +5 -5
  350. package/src/rbac/components/SecureDataProvider.tsx +5 -5
  351. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +8 -8
  352. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +14 -14
  353. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +12 -12
  354. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +6 -6
  355. package/src/rbac/engine.test.simple.ts +19 -13
  356. package/src/rbac/engine.test.ts +1 -0
  357. package/src/rbac/engine.ts +330 -766
  358. package/src/rbac/errors.ts +156 -0
  359. package/src/rbac/hooks/usePermissions.ts +32 -10
  360. package/src/rbac/hooks/useRBAC.test.ts +126 -512
  361. package/src/rbac/hooks/useRBAC.ts +147 -193
  362. package/src/rbac/hooks/useResolvedScope.ts +12 -0
  363. package/src/rbac/index.ts +7 -4
  364. package/src/rbac/security.ts +109 -18
  365. package/src/rbac/types.ts +12 -1
  366. package/src/services/AuthService.ts +2 -15
  367. package/src/services/EventService.ts +43 -46
  368. package/src/services/OrganisationService.ts +51 -31
  369. package/src/services/__tests__/AuthService.test.ts +1 -1
  370. package/src/services/__tests__/EventService.test.ts +1 -1
  371. package/src/services/__tests__/OrganisationService.test.ts +1 -1
  372. package/src/services/base/BaseService.ts +8 -0
  373. package/src/styles/base.css +208 -0
  374. package/src/styles/semantic.css +24 -0
  375. package/src/types/database.generated.ts +7347 -0
  376. package/src/types/database.ts +20 -0
  377. package/src/utils/logger.ts +179 -0
  378. package/src/utils/organisationContext.ts +11 -4
  379. package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
  380. package/dist/appNameResolver-UURKN7NF.js +0 -22
  381. package/dist/audit-6TOCAMKO.js.map +0 -1
  382. package/dist/chunk-B2WTCLCV.js.map +0 -1
  383. package/dist/chunk-FGMFQSHX.js.map +0 -1
  384. package/dist/chunk-K34IM5CT.js.map +0 -1
  385. package/dist/chunk-KHJS6VIA.js.map +0 -1
  386. package/dist/chunk-KK73ZB4E.js.map +0 -1
  387. package/dist/chunk-M5IWZRBT.js.map +0 -1
  388. package/dist/chunk-ULBI5JGB.js +0 -109
  389. package/dist/chunk-ULBI5JGB.js.map +0 -1
  390. package/dist/chunk-WN6XJWOS.js.map +0 -1
  391. package/dist/chunk-XLZ7U46Z.js.map +0 -1
  392. package/dist/chunk-Y6TXWPJO.js.map +0 -1
  393. package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
  394. package/docs/TERMINOLOGY.md +0 -231
  395. package/docs/api/interfaces/RBACContextType.md +0 -468
  396. package/docs/api/interfaces/RBACProviderProps.md +0 -107
  397. package/docs/best-practices/performance-expansion.md +0 -473
  398. package/docs/breaking-changes.md +0 -179
  399. package/docs/consuming-app-example.md +0 -290
  400. package/docs/documentation-templates.md +0 -539
  401. package/docs/examples/navigation-menu-auth-fix.md +0 -344
  402. package/docs/getting-started/examples/basic-auth-app.md +0 -520
  403. package/docs/getting-started/examples/full-featured-app.md +0 -616
  404. package/docs/getting-started/quick-start.md +0 -376
  405. package/docs/implementation-guides/datatable-filtering.md +0 -313
  406. package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
  407. package/docs/implementation-guides/hierarchical-datatable.md +0 -850
  408. package/docs/implementation-guides/large-datasets.md +0 -281
  409. package/docs/implementation-guides/performance.md +0 -403
  410. package/docs/migration/quick-migration-guide.md +0 -320
  411. package/docs/migration-guide.md +0 -193
  412. package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
  413. package/docs/performance/README.md +0 -551
  414. package/docs/style-guide.md +0 -964
  415. package/docs/troubleshooting/authentication-issues.md +0 -334
  416. package/docs/troubleshooting/debugging.md +0 -1117
  417. package/docs/troubleshooting/migration.md +0 -918
  418. package/src/__tests__/hooks/usePermissions.test.ts +0 -261
  419. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
  420. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
  421. package/src/hooks/services/__tests__/useServiceHooks.test.tsx +0 -137
  422. package/src/hooks/services/usePermissions.ts +0 -70
  423. package/src/hooks/services/useRBACService.ts +0 -30
  424. package/src/hooks/usePermissionCheck.ts +0 -150
  425. package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
  426. package/src/providers/services/RBACServiceProvider.tsx +0 -79
  427. package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
  428. package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
  429. package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
  430. package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
  431. package/src/rbac/providers/RBACProvider.tsx +0 -645
  432. package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
  433. package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
  434. package/src/rbac/providers/index.ts +0 -11
  435. package/src/services/RBACService.ts +0 -522
  436. package/src/services/__tests__/RBACService.test.ts +0 -492
  437. package/src/services/interfaces/IRBACService.ts +0 -62
  438. package/src/utils/appNameResolver.test 2.ts +0 -494
  439. /package/dist/{DataTable-4GAVPIEG.js.map → DataTable-ETGVF4Y5.js.map} +0 -0
  440. /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-P5SOJAQ6.js.map} +0 -0
  441. /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
  442. /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
  443. /package/dist/{chunk-NTNILOBC.js.map → chunk-5BO3MI5Y.js.map} +0 -0
  444. /package/dist/{chunk-URUTVZ7N.js.map → chunk-FL4ZCQLD.js.map} +0 -0
  445. /package/dist/{chunk-LW7MMEAQ.js.map → chunk-FT2M4R4F.js.map} +0 -0
  446. /package/dist/{chunk-AFGTSUAD.js.map → chunk-VSOKOFRF.js.map} +0 -0
  447. /package/docs/{app.css.example → styles/app.css.example} +0 -0
@@ -4,111 +4,84 @@
4
4
  * @module RBAC/Hooks
5
5
  * @since 0.3.0
6
6
  *
7
- * A React hook that provides access to the new RBAC (Role-Based Access Control) system.
8
- * This hook integrates with the database to provide real-time role and permission information.
9
- *
10
- * Features:
11
- * - Real-time role detection (global, organisation, event-app)
12
- * - Permission checking with database validation
13
- * - Hierarchical permission resolution
14
- * - Loading states and error handling
15
- * - Type-safe permission operations
16
- * - Automatic context detection
17
- *
18
- * @example
19
- * ```tsx
20
- * import { useRBAC } from '@jmruthers/pace-core/rbac';
21
- *
22
- * function MyComponent() {
23
- * const {
24
- * globalRole,
25
- * organisationRole,
26
- * eventAppRole,
27
- * hasPermission,
28
- * isSuperAdmin,
29
- * isLoading,
30
- * error
31
- * } = useRBAC();
32
- *
33
- * if (isLoading) return <div>Loading permissions...</div>;
34
- * if (error) return <div>Error: {error.message}</div>;
35
- *
36
- * return (
37
- * <div>
38
- * {isSuperAdmin && <AdminPanel />}
39
- * {hasPermission('read', 'dashboard') && <Dashboard />}
40
- * {hasPermission('create', 'events') && <CreateEventButton />}
41
- * </div>
42
- * );
43
- * }
44
- * ```
45
- *
46
- * @accessibility
47
- * - No direct accessibility concerns (hook)
48
- * - Enables accessible permission-based UI rendering
49
- * - Supports screen reader friendly conditional content
50
- *
51
- * @security
52
- * - Database-backed permission validation
53
- * - Hierarchical permission resolution
54
- * - Organisation context enforcement
55
- * - Real-time permission updates
56
- *
57
- * @performance
58
- * - Optimized with useMemo and useCallback
59
- * - Permission caching
60
- * - Minimal re-renders
61
- * - Lazy loading of permissions
62
- *
63
- * @dependencies
64
- * - React 18+ - Hooks and effects
65
- * - @supabase/supabase-js - Database integration
66
- * - RBAC types - Type definitions
7
+ * A React hook that provides access to the RBAC (Role-Based Access Control) system
8
+ * through the hardened RBAC engine API. The hook defers all permission and role
9
+ * resolution to the shared engine to ensure consistent security behaviour across
10
+ * applications.
67
11
  */
68
12
 
69
13
  import { useState, useEffect, useCallback, useMemo } from 'react';
70
14
  import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
71
15
  import { useOrganisations } from '../../hooks/useOrganisations';
72
16
  import { useEvents } from '../../hooks/useEvents';
73
- import type {
74
- UserRBACContext,
75
- GlobalRole,
76
- OrganisationRole,
77
- EventAppRole,
17
+ import {
18
+ getPermissionMap,
19
+ getAccessLevel,
20
+ isPermittedCached,
21
+ resolveAppContext,
22
+ getRoleContext,
23
+ } from '../api';
24
+ import { getRBACLogger } from '../config';
25
+ import type {
26
+ UserRBACContext,
27
+ GlobalRole,
28
+ OrganisationRole,
29
+ EventAppRole,
78
30
  Operation,
79
- RBACPermission
31
+ Permission,
32
+ Scope,
33
+ PermissionMap,
34
+ UUID,
80
35
  } from '../types';
81
36
 
37
+ function mapAccessLevelToEventRole(level: string | null): EventAppRole | null {
38
+ switch (level) {
39
+ case 'viewer':
40
+ return 'viewer';
41
+ case 'participant':
42
+ return 'participant';
43
+ case 'planner':
44
+ return 'planner';
45
+ case 'admin':
46
+ case 'super':
47
+ return 'event_admin';
48
+ default:
49
+ return null;
50
+ }
51
+ }
52
+
82
53
  export function useRBAC(pageId?: string): UserRBACContext {
83
- const { user, session, supabase, appName } = useUnifiedAuth();
54
+ const logger = getRBACLogger();
55
+ const { user, session, appName } = useUnifiedAuth();
84
56
  const { selectedOrganisation } = useOrganisations();
85
-
86
- // Try to get events context, but don't fail if not available
87
- let selectedEvent = null;
57
+
58
+ let selectedEvent: { event_id: string } | null = null;
88
59
  try {
89
60
  const eventsContext = useEvents();
90
61
  selectedEvent = eventsContext.selectedEvent;
91
62
  } catch (error) {
92
- // EventProvider not available, continue without event context
93
- console.debug('useRBAC: EventProvider not available, continuing without event context');
63
+ logger.debug('[useRBAC] Event provider not available, continuing without event context', error);
94
64
  }
95
-
96
- // State
65
+
97
66
  const [globalRole, setGlobalRole] = useState<GlobalRole | null>(null);
98
67
  const [organisationRole, setOrganisationRole] = useState<OrganisationRole | null>(null);
99
68
  const [eventAppRole, setEventAppRole] = useState<EventAppRole | null>(null);
100
- const [permissions, setPermissions] = useState<RBACPermission[]>([]);
69
+ const [permissionMap, setPermissionMap] = useState<PermissionMap>({} as PermissionMap);
70
+ const [currentScope, setCurrentScope] = useState<Scope | null>(null);
101
71
  const [isLoading, setIsLoading] = useState(false);
102
72
  const [error, setError] = useState<Error | null>(null);
103
73
 
104
- // Load RBAC context
74
+ const resetState = useCallback(() => {
75
+ setGlobalRole(null);
76
+ setOrganisationRole(null);
77
+ setEventAppRole(null);
78
+ setPermissionMap({} as PermissionMap);
79
+ setCurrentScope(null);
80
+ }, []);
81
+
105
82
  const loadRBACContext = useCallback(async () => {
106
- if (!user || !session || !supabase || !appName) {
107
- console.log('[useRBAC] Missing required dependencies, clearing RBAC state');
108
- setGlobalRole(null);
109
- setOrganisationRole(null);
110
- setEventAppRole(null);
111
- setPermissions([]);
83
+ if (!user || !session) {
84
+ resetState();
112
85
  return;
113
86
  }
114
87
 
@@ -116,144 +89,125 @@ export function useRBAC(pageId?: string): UserRBACContext {
116
89
  setError(null);
117
90
 
118
91
  try {
119
- // First resolve app name to app_id using secure RPC function
120
- const { data: appData, error: appError } = await supabase.rpc('util_app_resolve', {
121
- p_app_name: appName,
122
- p_user_id: user.id
123
- });
124
-
125
- if (appError || !appData || appData.length === 0) {
126
- console.warn('App not found or inactive:', appName);
127
- setIsLoading(false);
128
- return;
129
- }
130
-
131
- const app = appData[0];
132
- if (!app.has_access) {
133
- console.warn('User does not have access to app:', appName);
134
- setIsLoading(false);
135
- return;
92
+ let appId: UUID | undefined;
93
+ if (appName) {
94
+ const resolved = await resolveAppContext({ userId: user.id as UUID, appName });
95
+ if (!resolved || !resolved.hasAccess) {
96
+ throw new Error(`User does not have access to app "${appName}"`);
97
+ }
98
+ appId = resolved.appId;
136
99
  }
137
100
 
138
- const { data, error: rpcError } = await supabase.rpc('rbac_permissions_get', {
139
- p_user_id: user.id,
140
- p_app_id: app.app_id,
141
- p_event_id: selectedEvent?.event_id || null,
142
- p_organisation_id: selectedOrganisation?.id || null
143
- });
144
-
145
- if (rpcError) {
146
- throw new Error(`Failed to load RBAC permissions: ${rpcError.message}`);
147
- }
148
-
149
- if (data) {
150
- // Extract roles from permissions based on permission_type
151
- const globalPerms = data.filter((p: RBACPermission) => p.permission_type === 'all_permissions');
152
- const orgPerms = data.filter((p: RBACPermission) => p.permission_type === 'organisation_access');
153
- const eventPerms = data.filter((p: RBACPermission) => p.permission_type === 'event_app_access');
154
-
155
- // Set roles (take the highest permission for each type)
156
- setGlobalRole(globalPerms.length > 0 ? 'super_admin' : null);
157
- setOrganisationRole(orgPerms.length > 0 ? orgPerms[0].role_name as OrganisationRole : null);
158
- setEventAppRole(eventPerms.length > 0 ? eventPerms[0].role_name as EventAppRole : null);
159
- setPermissions(data);
160
- } else {
161
- setPermissions([]);
162
- }
101
+ const scope: Scope = {
102
+ organisationId: selectedOrganisation?.id,
103
+ eventId: selectedEvent?.event_id || undefined,
104
+ appId,
105
+ };
106
+ setCurrentScope(scope);
107
+
108
+ const [map, roleContext, accessLevel] = await Promise.all([
109
+ getPermissionMap({ userId: user.id as UUID, scope }),
110
+ getRoleContext({ userId: user.id as UUID, scope }),
111
+ getAccessLevel({ userId: user.id as UUID, scope }),
112
+ ]);
113
+
114
+ setPermissionMap(map);
115
+ setGlobalRole(roleContext.globalRole);
116
+ setOrganisationRole(roleContext.organisationRole);
117
+ setEventAppRole(roleContext.eventAppRole || mapAccessLevelToEventRole(accessLevel));
163
118
  } catch (err) {
164
- console.error('RBAC Context Loading Error:', err);
165
- setError(err instanceof Error ? err : new Error('Unknown error loading RBAC context'));
166
- setPermissions([]);
119
+ const handledError = err instanceof Error ? err : new Error('Failed to load RBAC context');
120
+ logger.error('[useRBAC] Error loading RBAC context:', handledError);
121
+ setError(handledError);
122
+ resetState();
167
123
  } finally {
168
124
  setIsLoading(false);
169
125
  }
170
- }, [user?.id, session, supabase, appName, selectedEvent?.event_id, selectedOrganisation?.id, pageId]);
126
+ }, [appName, logger, resetState, selectedEvent?.event_id, selectedOrganisation?.id, session, user]);
171
127
 
172
- // Permission checking function
173
- const hasPermission = useCallback(async (operation: Operation, targetPageId?: string): Promise<boolean> => {
174
- if (!user || !session || !supabase || !appName) return false;
128
+ const hasPermission = useCallback(
129
+ async (operationOrPermission: Operation | string, targetPageId?: string): Promise<boolean> => {
130
+ if (!user) {
131
+ logger.warn('[useRBAC] Permission check attempted without authenticated user context');
132
+ return false;
133
+ }
175
134
 
176
- // Super admin has all permissions
177
- if (globalRole === 'super_admin') {
178
- return true;
179
- }
135
+ if (!currentScope || !currentScope.organisationId) {
136
+ logger.error('[useRBAC] Permission check denied due to missing organisation context', {
137
+ hasScope: !!currentScope,
138
+ scope: currentScope,
139
+ userId: user.id,
140
+ permission: operationOrPermission,
141
+ });
142
+ return false;
143
+ }
180
144
 
181
- try {
182
- // First resolve app name to app_id using secure RPC function
183
- const { data: appData, error: appError } = await supabase.rpc('util_app_resolve', {
184
- p_app_name: appName,
185
- p_user_id: user.id
186
- });
145
+ if (globalRole === 'super_admin' || permissionMap['*']) {
146
+ logger.info('[useRBAC] Super admin bypass granted', {
147
+ userId: user.id,
148
+ permission: operationOrPermission,
149
+ });
150
+ return true;
151
+ }
187
152
 
188
- if (appError || !appData || appData.length === 0) {
189
- console.warn('App not found or inactive:', appName);
190
- return false;
153
+ let permission: Permission;
154
+ if (operationOrPermission.includes(':')) {
155
+ permission = operationOrPermission as Permission;
156
+ } else if (targetPageId || pageId) {
157
+ permission = `${operationOrPermission}:${targetPageId || pageId}` as Permission;
158
+ } else {
159
+ permission = operationOrPermission as Permission;
191
160
  }
192
161
 
193
- const app = appData[0];
194
- if (!app.has_access) {
195
- console.warn('User does not have access to app:', appName);
162
+ const cachedValue = permissionMap[permission];
163
+ if (cachedValue === true) {
164
+ return true;
165
+ }
166
+ if (cachedValue === false) {
196
167
  return false;
197
168
  }
198
169
 
199
- const { data, error } = await supabase.rpc('rbac_page_access_check', {
200
- p_user_id: user.id,
201
- p_app_id: app.app_id,
202
- p_page_id: targetPageId || pageId || 'default',
203
- p_operation: operation,
204
- p_event_id: selectedEvent?.event_id,
205
- p_organisation_id: selectedOrganisation?.id
170
+ return isPermittedCached({
171
+ userId: user.id as UUID,
172
+ scope: currentScope,
173
+ permission,
174
+ pageId: targetPageId ?? pageId,
206
175
  });
176
+ },
177
+ [currentScope, globalRole, logger, pageId, permissionMap, user],
178
+ );
207
179
 
208
- if (error) {
209
- console.error('Permission check failed:', error);
210
- return false;
180
+ const hasGlobalPermission = useCallback(
181
+ (permission: string): boolean => {
182
+ if (globalRole === 'super_admin' || permissionMap['*']) {
183
+ return true;
211
184
  }
212
185
 
213
- return data === true;
214
- } catch (err) {
215
- console.error('Permission check error:', err);
216
- return false;
217
- }
218
- }, [user, supabase, appName, selectedEvent, selectedOrganisation, pageId, globalRole]);
186
+ if (permission === 'super_admin') {
187
+ return globalRole === 'super_admin';
188
+ }
219
189
 
220
- // Simple permission check for global roles (synchronous)
221
- const hasGlobalPermission = useCallback((permission: string): boolean => {
222
- // Super admin has all permissions
223
- if (globalRole === 'super_admin') {
224
- return true;
225
- }
226
-
227
- // Check specific global roles
228
- if (permission === 'super_admin') {
229
- return globalRole === 'super_admin';
230
- }
231
-
232
- if (permission === 'org_admin') {
233
- return organisationRole === 'org_admin' || globalRole === 'super_admin';
234
- }
235
-
236
- return false;
237
- }, [globalRole, organisationRole]);
190
+ if (permission === 'org_admin') {
191
+ return organisationRole === 'org_admin';
192
+ }
238
193
 
239
- // Computed properties
240
- const isSuperAdmin = useMemo(() => globalRole === 'super_admin', [globalRole]);
241
- const isOrgAdmin = useMemo(() => organisationRole === 'org_admin', [organisationRole]);
242
- const isEventAdmin = useMemo(() => eventAppRole === 'event_admin', [eventAppRole]);
243
- const canManageOrganisation = useMemo(() =>
244
- isSuperAdmin || isOrgAdmin, [isSuperAdmin, isOrgAdmin]
245
- );
246
- const canManageEvent = useMemo(() =>
247
- isSuperAdmin || isEventAdmin, [isSuperAdmin, isEventAdmin]
194
+ return permissionMap[permission as Permission] === true;
195
+ },
196
+ [globalRole, organisationRole, permissionMap],
248
197
  );
249
198
 
250
- // Load context when dependencies change
199
+ const isSuperAdmin = useMemo(() => globalRole === 'super_admin' || permissionMap['*'] === true, [globalRole, permissionMap]);
200
+ const isOrgAdmin = useMemo(() => organisationRole === 'org_admin' || isSuperAdmin, [organisationRole, isSuperAdmin]);
201
+ const isEventAdmin = useMemo(() => eventAppRole === 'event_admin' || isSuperAdmin, [eventAppRole, isSuperAdmin]);
202
+ const canManageOrganisation = useMemo(() => isSuperAdmin || organisationRole === 'org_admin', [isSuperAdmin, organisationRole]);
203
+ const canManageEvent = useMemo(() => isSuperAdmin || eventAppRole === 'event_admin', [isSuperAdmin, eventAppRole]);
204
+
251
205
  useEffect(() => {
252
206
  loadRBACContext();
253
207
  }, [loadRBACContext]);
254
208
 
255
209
  return {
256
- user, // Add user to the returned context
210
+ user,
257
211
  globalRole,
258
212
  organisationRole,
259
213
  eventAppRole,
@@ -265,6 +219,6 @@ export function useRBAC(pageId?: string): UserRBACContext {
265
219
  canManageOrganisation,
266
220
  canManageEvent,
267
221
  isLoading,
268
- error
222
+ error,
269
223
  };
270
224
  }
@@ -144,8 +144,17 @@ export function useResolvedScope({
144
144
  }
145
145
  }
146
146
 
147
+ // Debug logging
148
+ console.log('[useResolvedScope] Attempting to resolve scope:', {
149
+ selectedOrganisationId,
150
+ selectedEventId,
151
+ appId: appId || 'NOT RESOLVED YET',
152
+ resolveStep: 'initial'
153
+ });
154
+
147
155
  // If we have both organisation and event, use them directly
148
156
  if (selectedOrganisationId && selectedEventId) {
157
+ console.log('[useResolvedScope] Resolving with both org and event');
149
158
  if (!cancelled) {
150
159
  setResolvedScope({
151
160
  organisationId: selectedOrganisationId,
@@ -159,6 +168,7 @@ export function useResolvedScope({
159
168
 
160
169
  // If we only have organisation, use it
161
170
  if (selectedOrganisationId) {
171
+ console.log('[useResolvedScope] Resolving with organisation only');
162
172
  if (!cancelled) {
163
173
  setResolvedScope({
164
174
  organisationId: selectedOrganisationId,
@@ -173,6 +183,7 @@ export function useResolvedScope({
173
183
  // If we only have event, resolve organisation from event
174
184
  if (selectedEventId && supabase) {
175
185
  try {
186
+ console.log('[useResolvedScope] Resolving from event:', { selectedEventId, appId });
176
187
  const eventScope = await createScopeFromEvent(supabase, selectedEventId, appId);
177
188
  if (!eventScope) {
178
189
  console.error('[useResolvedScope] Could not resolve organization from event context');
@@ -183,6 +194,7 @@ export function useResolvedScope({
183
194
  }
184
195
  return;
185
196
  }
197
+ console.log('[useResolvedScope] Resolved from event:', eventScope);
186
198
  // Preserve the resolved app ID
187
199
  if (!cancelled) {
188
200
  setResolvedScope({
package/src/rbac/index.ts CHANGED
@@ -71,14 +71,15 @@ export {
71
71
  createRBACEngine,
72
72
  } from './engine';
73
73
 
74
- // Components (NEW - Phase 1 & 2)
74
+ // Components
75
75
  export * from './components';
76
76
 
77
- // Hooks - Consolidated from rbac/hooks/
77
+ // Hooks
78
78
  export * from './hooks';
79
79
 
80
- // Providers - Consolidated from rbac/providers/
81
- export * from './providers';
80
+ // Providers - Note: RBACProvider was removed as part of the refactoring
81
+ // Users should now use useRBAC() hook directly without a provider wrapper
82
+ // export * from './providers';
82
83
 
83
84
  // Adapters
84
85
  export {
@@ -97,6 +98,8 @@ export {
97
98
  export {
98
99
  getAccessLevel,
99
100
  getPermissionMap,
101
+ resolveAppContext,
102
+ getRoleContext,
100
103
  isPermitted,
101
104
  isPermittedCached,
102
105
  hasPermission,
@@ -176,7 +176,17 @@ export class RBACSecurityValidator {
176
176
  * @param event - Security event details
177
177
  */
178
178
  static logSecurityEvent(event: {
179
- type: 'permission_denied' | 'invalid_input' | 'rate_limit_exceeded' | 'suspicious_activity';
179
+ type:
180
+ | 'permission_denied'
181
+ | 'invalid_input'
182
+ | 'rate_limit_exceeded'
183
+ | 'suspicious_activity'
184
+ | 'network_error'
185
+ | 'database_error'
186
+ | 'validation_error'
187
+ | 'rate_limit_error'
188
+ | 'authentication_error'
189
+ | 'unknown_error';
180
190
  userId: UUID;
181
191
  details: Record<string, any>;
182
192
  timestamp?: Date;
@@ -205,10 +215,17 @@ export class RBACSecurityValidator {
205
215
  case 'permission_denied':
206
216
  return 'low';
207
217
  case 'invalid_input':
208
- return 'medium';
209
218
  case 'rate_limit_exceeded':
219
+ case 'rate_limit_error':
220
+ case 'network_error':
210
221
  return 'medium';
222
+ case 'validation_error':
223
+ return 'high';
224
+ case 'authentication_error':
225
+ case 'database_error':
226
+ return 'critical';
211
227
  case 'suspicious_activity':
228
+ case 'unknown_error':
212
229
  return 'high';
213
230
  default:
214
231
  return 'low';
@@ -243,7 +260,7 @@ export const DEFAULT_SECURITY_CONFIG: RBACSecurityConfig = {
243
260
  */
244
261
  export interface SecurityContext {
245
262
  userId: UUID;
246
- organisationId: UUID;
263
+ organisationId?: UUID; // Optional for operations without organisation context
247
264
  ipAddress?: string;
248
265
  userAgent?: string;
249
266
  timestamp: Date;
@@ -257,6 +274,17 @@ export class RBACSecurityMiddleware {
257
274
 
258
275
  constructor(config: RBACSecurityConfig = DEFAULT_SECURITY_CONFIG) {
259
276
  this.config = config;
277
+ this._startCleanupInterval();
278
+ }
279
+
280
+ /**
281
+ * Start periodic cleanup of expired entries
282
+ */
283
+ private _startCleanupInterval(): void {
284
+ // Clear expired entries every 5 minutes
285
+ setInterval(() => {
286
+ this.clearExpiredEntries();
287
+ }, 5 * 60 * 1000);
260
288
  }
261
289
 
262
290
  /**
@@ -271,30 +299,33 @@ export class RBACSecurityMiddleware {
271
299
  }> {
272
300
  const errors: string[] = [];
273
301
 
274
- if (!this.config.enableInputValidation) {
275
- return { isValid: true, errors: [] };
276
- }
277
-
278
- // Validate user ID
302
+ // Core validations are always enforced regardless of configuration
279
303
  if (!RBACSecurityValidator.validateUserId(context.userId)) {
280
304
  errors.push('Invalid user ID format');
281
305
  }
282
306
 
283
- // Validate organisation ID
284
- if (!RBACSecurityValidator.validateUUID(context.organisationId)) {
307
+ if (context.organisationId && !RBACSecurityValidator.validateUUID(context.organisationId)) {
285
308
  errors.push('Invalid organisation ID format');
286
309
  }
287
310
 
288
- // Validate permission if present
289
311
  if (input.permission && !RBACSecurityValidator.validatePermission(input.permission)) {
290
312
  errors.push('Invalid permission format');
291
313
  }
292
314
 
293
- // Validate scope if present
294
315
  if (input.scope && !RBACSecurityValidator.validateScope(input.scope)) {
295
316
  errors.push('Invalid scope format');
296
317
  }
297
318
 
319
+ if (this.config.enableInputValidation) {
320
+ if (context.ipAddress && typeof context.ipAddress !== 'string') {
321
+ errors.push('Invalid IP address format');
322
+ }
323
+
324
+ if (context.userAgent && typeof context.userAgent !== 'string') {
325
+ errors.push('Invalid user agent format');
326
+ }
327
+ }
328
+
298
329
  // Log suspicious activity
299
330
  if (errors.length > 0) {
300
331
  RBACSecurityValidator.logSecurityEvent({
@@ -323,18 +354,78 @@ export class RBACSecurityMiddleware {
323
354
  return { isAllowed: true, remaining: this.config.maxPermissionChecksPerMinute };
324
355
  }
325
356
 
326
- // TODO: Implement actual rate limiting logic
327
- const isAllowed = await RBACSecurityValidator.checkRateLimit(
328
- context.userId,
329
- 'permission_check'
330
- );
357
+ // Implementation: In-memory rate limiting with sliding window
358
+ // For production, consider using Redis or Supabase Edge Functions
359
+ const isAllowed = await this._checkRateLimitInternal(context.userId);
360
+
361
+ const remaining = isAllowed ? this.config.maxPermissionChecksPerMinute - this._getRequestCount(context.userId) : 0;
331
362
 
332
363
  return {
333
364
  isAllowed,
334
- remaining: this.config.maxPermissionChecksPerMinute,
365
+ remaining: Math.max(0, remaining),
335
366
  };
336
367
  }
337
368
 
369
+ /**
370
+ * In-memory rate limiting cache (sliding window)
371
+ * Note: For production, this should use Redis or Supabase Edge Functions
372
+ */
373
+ private rateLimitCache = new Map<UUID, Array<{ timestamp: number }>>();
374
+
375
+ private async _checkRateLimitInternal(userId: UUID): Promise<boolean> {
376
+ const now = Date.now();
377
+ const windowMs = 60 * 1000; // 1 minute window
378
+
379
+ // Get or create rate limit entries for this user
380
+ const entries = this.rateLimitCache.get(userId) || [];
381
+
382
+ // Remove entries outside the time window
383
+ const validEntries = entries.filter(entry => now - entry.timestamp < windowMs);
384
+
385
+ // Check if user exceeded rate limit
386
+ const requestCount = validEntries.length;
387
+ const isAllowed = requestCount < this.config.maxPermissionChecksPerMinute;
388
+
389
+ // If allowed, add current request
390
+ if (isAllowed) {
391
+ validEntries.push({ timestamp: now });
392
+ }
393
+
394
+ // Update cache
395
+ this.rateLimitCache.set(userId, validEntries);
396
+
397
+ return isAllowed;
398
+ }
399
+
400
+ private _getRequestCount(userId: UUID): number {
401
+ const now = Date.now();
402
+ const windowMs = 60 * 1000; // 1 minute window
403
+
404
+ const entries = this.rateLimitCache.get(userId) || [];
405
+ const validEntries = entries.filter(entry => now - entry.timestamp < windowMs);
406
+
407
+ return validEntries.length;
408
+ }
409
+
410
+ /**
411
+ * Clear old rate limit entries to prevent memory leaks
412
+ * Should be called periodically (e.g., every 5 minutes)
413
+ */
414
+ private clearExpiredEntries(): void {
415
+ const now = Date.now();
416
+ const windowMs = 60 * 1000; // 1 minute window
417
+
418
+ for (const [userId, entries] of this.rateLimitCache.entries()) {
419
+ const validEntries = entries.filter(entry => now - entry.timestamp < windowMs);
420
+
421
+ if (validEntries.length === 0) {
422
+ this.rateLimitCache.delete(userId);
423
+ } else {
424
+ this.rateLimitCache.set(userId, validEntries);
425
+ }
426
+ }
427
+ }
428
+
338
429
  /**
339
430
  * Sanitize input data
340
431
  * @param input - Input to sanitize