@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
@@ -1,573 +1,187 @@
1
- /**
2
- * @file useRBAC Hook Tests
3
- * @package @jmruthers/pace-core
4
- * @module Hooks/useRBAC
5
- * @since 0.3.0
6
- *
7
- * Comprehensive tests for the useRBAC hook covering all critical functionality.
8
- */
9
-
10
1
  import { renderHook, waitFor } from '@testing-library/react';
11
- import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
12
3
  import { useRBAC } from './useRBAC';
13
4
 
14
- // Mock the providers and dependencies
15
5
  vi.mock('../../providers/UnifiedAuthProvider', () => ({
16
- useUnifiedAuth: vi.fn()
6
+ useUnifiedAuth: vi.fn(),
17
7
  }));
18
8
 
19
9
  vi.mock('../../providers/services/UnifiedAuthProvider', () => ({
20
- useUnifiedAuth: vi.fn()
10
+ useUnifiedAuth: vi.fn(),
21
11
  }));
22
12
 
23
13
  vi.mock('../../hooks/useOrganisations', () => ({
24
- useOrganisations: vi.fn()
14
+ useOrganisations: vi.fn(),
25
15
  }));
26
16
 
27
17
  vi.mock('../../hooks/useEvents', () => ({
28
- useEvents: vi.fn()
18
+ useEvents: vi.fn(),
29
19
  }));
30
20
 
31
- // Mock Supabase client
32
- const mockSupabaseClient = {
33
- rpc: vi.fn()
34
- };
21
+ vi.mock('../api', () => ({
22
+ getPermissionMap: vi.fn(),
23
+ getAccessLevel: vi.fn(),
24
+ isPermittedCached: vi.fn(),
25
+ resolveAppContext: vi.fn(),
26
+ getRoleContext: vi.fn(),
27
+ }));
35
28
 
36
- // Get the mocked functions
37
29
  import { useUnifiedAuth } from '../../providers';
38
30
  import { useOrganisations } from '../../hooks/useOrganisations';
39
31
  import { useEvents } from '../../hooks/useEvents';
32
+ import {
33
+ getPermissionMap,
34
+ getAccessLevel,
35
+ isPermittedCached,
36
+ resolveAppContext,
37
+ getRoleContext,
38
+ } from '../api';
40
39
 
41
40
  const mockUseUnifiedAuth = vi.mocked(useUnifiedAuth);
42
41
  const mockUseOrganisations = vi.mocked(useOrganisations);
43
42
  const mockUseEvents = vi.mocked(useEvents);
43
+ const mockGetPermissionMap = vi.mocked(getPermissionMap);
44
+ const mockGetAccessLevel = vi.mocked(getAccessLevel);
45
+ const mockIsPermittedCached = vi.mocked(isPermittedCached);
46
+ const mockResolveAppContext = vi.mocked(resolveAppContext);
47
+ const mockGetRoleContext = vi.mocked(getRoleContext);
44
48
 
45
- describe('useRBAC Hook', () => {
46
- const mockUser = {
47
- id: 'user-123',
48
- email: 'test@example.com'
49
- };
50
-
51
- const mockSession = {
52
- access_token: 'mock-token'
53
- };
54
-
55
- const mockOrganisation = {
56
- id: 'org-123',
57
- name: 'Test Organisation'
58
- };
59
-
60
- const mockEvent = {
61
- id: 'event-123',
62
- name: 'Test Event'
63
- };
64
-
49
+ describe('useRBAC', () => {
65
50
  beforeEach(() => {
66
51
  vi.clearAllMocks();
67
-
68
- // Setup default mocks
52
+
69
53
  mockUseUnifiedAuth.mockReturnValue({
70
54
  user: null,
71
55
  session: null,
72
- supabase: mockSupabaseClient,
73
- appName: 'test-app'
56
+ appName: 'test-app',
74
57
  });
75
-
76
- mockUseOrganisations.mockReturnValue({
77
- selectedOrganisation: null
78
- });
79
-
80
- mockUseEvents.mockReturnValue({
81
- selectedEvent: null
58
+
59
+ mockUseOrganisations.mockReturnValue({ selectedOrganisation: null });
60
+ mockUseEvents.mockReturnValue({ selectedEvent: null });
61
+
62
+ mockGetPermissionMap.mockResolvedValue({});
63
+ mockGetAccessLevel.mockResolvedValue('viewer');
64
+ mockResolveAppContext.mockResolvedValue({ appId: 'app-123' as any, hasAccess: true });
65
+ mockGetRoleContext.mockResolvedValue({
66
+ globalRole: null,
67
+ organisationRole: null,
68
+ eventAppRole: null,
82
69
  });
70
+ mockIsPermittedCached.mockResolvedValue(false);
83
71
  });
84
72
 
85
- describe('Initialization', () => {
86
- it('initializes with loading state when no user', () => {
87
- const { result } = renderHook(() => useRBAC());
88
-
89
- expect(result.current.isLoading).toBe(false);
90
- expect(result.current.user).toBeNull();
91
- expect(result.current.globalRole).toBeNull();
92
- expect(result.current.organisationRole).toBeNull();
93
- expect(result.current.eventAppRole).toBeNull();
94
- expect(result.current.error).toBeNull();
95
- });
73
+ it('returns base state when unauthenticated', () => {
74
+ const { result } = renderHook(() => useRBAC());
96
75
 
97
- it('initializes with authenticated state', async () => {
98
- mockUseUnifiedAuth.mockReturnValue({
99
- user: mockUser,
100
- session: mockSession,
101
- supabase: mockSupabaseClient,
102
- appName: 'test-app'
103
- });
104
-
105
- mockUseOrganisations.mockReturnValue({
106
- selectedOrganisation: mockOrganisation
107
- });
108
-
109
- mockUseEvents.mockReturnValue({
110
- selectedEvent: mockEvent
111
- });
112
-
113
- // Mock successful RPC calls
114
- mockSupabaseClient.rpc.mockImplementation((functionName: string) => {
115
- if (functionName === 'util_app_resolve') {
116
- return Promise.resolve({
117
- data: [{ app_id: 'test-app-id', has_access: true }],
118
- error: null
119
- });
120
- }
121
- if (functionName === 'rbac_permissions_get') {
122
- return Promise.resolve({
123
- data: [{ permission_type: 'all_permissions', role_name: 'super_admin' }],
124
- error: null
125
- });
126
- }
127
- return Promise.resolve({ data: null, error: null });
128
- });
129
-
130
- const { result } = renderHook(() => useRBAC());
131
-
132
- expect(result.current.user).toEqual(mockUser);
133
-
134
- await waitFor(() => {
135
- expect(result.current.isLoading).toBe(false);
136
- });
137
- });
76
+ expect(result.current.user).toBeNull();
77
+ expect(result.current.globalRole).toBeNull();
78
+ expect(result.current.isLoading).toBe(false);
138
79
  });
139
80
 
140
- describe('Role Detection', () => {
141
- it('detects super admin role from RPC response', async () => {
142
- mockUseUnifiedAuth.mockReturnValue({
143
- user: mockUser,
144
- session: mockSession,
145
- supabase: mockSupabaseClient,
146
- appName: 'test-app'
147
- });
148
-
149
- mockUseOrganisations.mockReturnValue({
150
- selectedOrganisation: mockOrganisation
151
- });
152
-
153
- // Mock super admin RPC response
154
- mockSupabaseClient.rpc.mockImplementation((functionName: string) => {
155
- if (functionName === 'util_app_resolve') {
156
- return Promise.resolve({
157
- data: [{ app_id: 'test-app-id', has_access: true }],
158
- error: null
159
- });
160
- }
161
- if (functionName === 'rbac_permissions_get') {
162
- return Promise.resolve({
163
- data: [{ permission_type: 'all_permissions', role_name: 'super_admin' }],
164
- error: null
165
- });
166
- }
167
- return Promise.resolve({ data: null, error: null });
168
- });
169
-
170
- const { result } = renderHook(() => useRBAC());
171
-
172
- await waitFor(() => {
173
- expect(result.current.globalRole).toBe('super_admin');
174
- expect(result.current.isSuperAdmin).toBe(true);
175
- });
81
+ it('loads permissions through RBAC engine', async () => {
82
+ mockUseUnifiedAuth.mockReturnValue({
83
+ user: { id: 'user-1' },
84
+ session: { access_token: 'token' },
85
+ appName: 'test-app',
176
86
  });
87
+ mockUseOrganisations.mockReturnValue({ selectedOrganisation: { id: 'org-1' } });
177
88
 
178
- it('detects organisation admin role', async () => {
179
- mockUseUnifiedAuth.mockReturnValue({
180
- user: mockUser,
181
- session: mockSession,
182
- supabase: mockSupabaseClient,
183
- appName: 'test-app'
184
- });
185
-
186
- mockUseOrganisations.mockReturnValue({
187
- selectedOrganisation: mockOrganisation
188
- });
189
-
190
- // Mock org admin RPC response
191
- mockSupabaseClient.rpc.mockImplementation((functionName: string) => {
192
- if (functionName === 'util_app_resolve') {
193
- return Promise.resolve({
194
- data: [{ app_id: 'test-app-id', has_access: true }],
195
- error: null
196
- });
197
- }
198
- if (functionName === 'rbac_permissions_get') {
199
- return Promise.resolve({
200
- data: [{ permission_type: 'organisation_access', role_name: 'org_admin' }],
201
- error: null
202
- });
203
- }
204
- return Promise.resolve({ data: null, error: null });
205
- });
206
-
207
- const { result } = renderHook(() => useRBAC());
208
-
209
- await waitFor(() => {
210
- expect(result.current.organisationRole).toBe('org_admin');
211
- expect(result.current.isOrgAdmin).toBe(true);
212
- });
89
+ mockGetPermissionMap.mockResolvedValue({ 'read:dashboard': true });
90
+ mockGetRoleContext.mockResolvedValue({
91
+ globalRole: null,
92
+ organisationRole: null,
93
+ eventAppRole: null,
213
94
  });
214
95
 
215
- it('detects event admin role', async () => {
216
- mockUseUnifiedAuth.mockReturnValue({
217
- user: mockUser,
218
- session: mockSession,
219
- supabase: mockSupabaseClient,
220
- appName: 'test-app'
221
- });
222
-
223
- mockUseOrganisations.mockReturnValue({
224
- selectedOrganisation: mockOrganisation
225
- });
226
-
227
- mockUseEvents.mockReturnValue({
228
- selectedEvent: mockEvent
229
- });
230
-
231
- // Mock event admin RPC response
232
- mockSupabaseClient.rpc.mockImplementation((functionName: string) => {
233
- if (functionName === 'util_app_resolve') {
234
- return Promise.resolve({
235
- data: [{ app_id: 'test-app-id', has_access: true }],
236
- error: null
237
- });
238
- }
239
- if (functionName === 'rbac_permissions_get') {
240
- return Promise.resolve({
241
- data: [{ permission_type: 'event_app_access', role_name: 'event_admin' }],
242
- error: null
243
- });
244
- }
245
- return Promise.resolve({ data: null, error: null });
246
- });
247
-
248
- const { result } = renderHook(() => useRBAC());
249
-
250
- await waitFor(() => {
251
- expect(result.current.eventAppRole).toBe('event_admin');
252
- expect(result.current.isEventAdmin).toBe(true);
253
- });
254
- });
96
+ const { result } = renderHook(() => useRBAC('dashboard'));
255
97
 
256
- it('handles role hierarchy correctly', async () => {
257
- mockUseUnifiedAuth.mockReturnValue({
258
- user: mockUser,
259
- session: mockSession,
260
- supabase: mockSupabaseClient,
261
- appName: 'test-app'
262
- });
263
-
264
- mockUseOrganisations.mockReturnValue({
265
- selectedOrganisation: mockOrganisation
266
- });
267
-
268
- mockUseEvents.mockReturnValue({
269
- selectedEvent: mockEvent
270
- });
271
-
272
- // Mock super admin with all roles
273
- mockSupabaseClient.rpc.mockImplementation((functionName: string) => {
274
- if (functionName === 'util_app_resolve') {
275
- return Promise.resolve({
276
- data: [{ app_id: 'test-app-id', has_access: true }],
277
- error: null
278
- });
279
- }
280
- if (functionName === 'rbac_permissions_get') {
281
- return Promise.resolve({
282
- data: [
283
- { permission_type: 'all_permissions', role_name: 'super_admin' },
284
- { permission_type: 'organisation_access', role_name: 'org_admin' },
285
- { permission_type: 'event_app_access', role_name: 'event_admin' }
286
- ],
287
- error: null
288
- });
289
- }
290
- return Promise.resolve({ data: null, error: null });
291
- });
292
-
293
- const { result } = renderHook(() => useRBAC());
294
-
295
- await waitFor(() => {
296
- expect(result.current.globalRole).toBe('super_admin');
297
- expect(result.current.organisationRole).toBe('org_admin');
298
- expect(result.current.eventAppRole).toBe('event_admin');
299
- expect(result.current.isSuperAdmin).toBe(true);
300
- expect(result.current.isOrgAdmin).toBe(true);
301
- expect(result.current.isEventAdmin).toBe(true);
302
- });
98
+ await waitFor(() => {
99
+ expect(result.current.isLoading).toBe(false);
100
+ expect(result.current.hasPermission).toBeTypeOf('function');
303
101
  });
102
+
103
+ expect(mockResolveAppContext).toHaveBeenCalledWith({ userId: 'user-1', appName: 'test-app' });
104
+ expect(mockGetPermissionMap).toHaveBeenCalledWith(
105
+ expect.objectContaining({
106
+ userId: 'user-1',
107
+ scope: expect.objectContaining({ organisationId: 'org-1' }),
108
+ }),
109
+ );
304
110
  });
305
111
 
306
- describe('Permission Checking', () => {
307
- it('hasPermission returns true for super admin', async () => {
308
- mockUseUnifiedAuth.mockReturnValue({
309
- user: mockUser,
310
- session: mockSession,
311
- supabase: mockSupabaseClient,
312
- appName: 'test-app'
313
- });
314
-
315
- mockUseOrganisations.mockReturnValue({
316
- selectedOrganisation: mockOrganisation
317
- });
318
-
319
- // Mock super admin RPC response
320
- mockSupabaseClient.rpc.mockImplementation((functionName: string) => {
321
- if (functionName === 'util_app_resolve') {
322
- return Promise.resolve({
323
- data: [{ app_id: 'test-app-id', has_access: true }],
324
- error: null
325
- });
326
- }
327
- if (functionName === 'rbac_permissions_get') {
328
- return Promise.resolve({
329
- data: [{ permission_type: 'all_permissions', role_name: 'super_admin' }],
330
- error: null
331
- });
332
- }
333
- return Promise.resolve({ data: null, error: null });
334
- });
335
-
336
- const { result } = renderHook(() => useRBAC());
337
-
338
- await waitFor(() => {
339
- expect(result.current.isSuperAdmin).toBe(true);
340
- });
341
-
342
- // Test hasPermission function
343
- const canRead = await result.current.hasPermission('read', 'users');
344
- expect(canRead).toBe(true);
112
+ it('uses cached permission map before hitting engine', async () => {
113
+ mockUseUnifiedAuth.mockReturnValue({
114
+ user: { id: 'user-1' },
115
+ session: { access_token: 'token' },
116
+ appName: 'test-app',
345
117
  });
118
+ mockUseOrganisations.mockReturnValue({ selectedOrganisation: { id: 'org-1' } });
119
+ mockGetPermissionMap.mockResolvedValue({ 'read:dashboard': true });
346
120
 
347
- it('hasPermission checks database for regular users', async () => {
348
- mockUseUnifiedAuth.mockReturnValue({
349
- user: mockUser,
350
- session: mockSession,
351
- supabase: mockSupabaseClient,
352
- appName: 'test-app'
353
- });
354
-
355
- mockUseOrganisations.mockReturnValue({
356
- selectedOrganisation: mockOrganisation
357
- });
358
-
359
- // Mock regular user RPC response
360
- mockSupabaseClient.rpc.mockImplementation((functionName: string) => {
361
- if (functionName === 'util_app_resolve') {
362
- return Promise.resolve({
363
- data: [{ app_id: 'test-app-id', has_access: true }],
364
- error: null
365
- });
366
- }
367
- if (functionName === 'rbac_permissions_get') {
368
- return Promise.resolve({
369
- data: [{ permission_type: 'organisation_access', role_name: 'member' }],
370
- error: null
371
- });
372
- }
373
- return Promise.resolve({ data: null, error: null });
374
- });
375
-
376
- const { result } = renderHook(() => useRBAC());
377
-
378
- await waitFor(() => {
379
- expect(result.current.organisationRole).toBe('member');
380
- });
381
-
382
- // Test hasPermission function
383
- const canDelete = await result.current.hasPermission('delete', 'users');
384
- expect(canDelete).toBe(false);
385
- });
121
+ const { result } = renderHook(() => useRBAC('dashboard'));
386
122
 
387
- it('hasGlobalPermission works correctly', async () => {
388
- mockUseUnifiedAuth.mockReturnValue({
389
- user: mockUser,
390
- session: mockSession,
391
- supabase: mockSupabaseClient,
392
- appName: 'test-app'
393
- });
394
-
395
- mockUseOrganisations.mockReturnValue({
396
- selectedOrganisation: mockOrganisation
397
- });
398
-
399
- // Mock super admin RPC response
400
- mockSupabaseClient.rpc.mockImplementation((functionName: string) => {
401
- if (functionName === 'util_app_resolve') {
402
- return Promise.resolve({
403
- data: [{ app_id: 'test-app-id', has_access: true }],
404
- error: null
405
- });
406
- }
407
- if (functionName === 'rbac_permissions_get') {
408
- return Promise.resolve({
409
- data: [{ permission_type: 'all_permissions', role_name: 'super_admin' }],
410
- error: null
411
- });
412
- }
413
- return Promise.resolve({ data: null, error: null });
414
- });
415
-
416
- const { result } = renderHook(() => useRBAC());
417
-
418
- await waitFor(() => {
419
- expect(result.current.globalRole).toBe('super_admin');
420
- });
421
-
422
- // Test hasGlobalPermission function
423
- expect(result.current.hasGlobalPermission('super_admin')).toBe(true);
424
- expect(result.current.hasGlobalPermission('org_admin')).toBe(true);
425
- expect(result.current.hasGlobalPermission('invalid_permission')).toBe(true); // Super admin has all permissions
426
- });
123
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
124
+
125
+ mockIsPermittedCached.mockClear();
126
+ const allowed = await result.current.hasPermission('read', 'dashboard');
127
+
128
+ expect(allowed).toBe(true);
129
+ expect(mockIsPermittedCached).not.toHaveBeenCalled();
427
130
  });
428
131
 
429
- describe('Error Handling', () => {
430
- it('handles RPC errors gracefully', async () => {
431
- mockUseUnifiedAuth.mockReturnValue({
432
- user: mockUser,
433
- session: mockSession,
434
- supabase: mockSupabaseClient,
435
- appName: 'test-app'
436
- });
132
+ it('falls back to engine when permission not cached', async () => {
133
+ mockUseUnifiedAuth.mockReturnValue({
134
+ user: { id: 'user-1' },
135
+ session: { access_token: 'token' },
136
+ appName: 'test-app',
137
+ });
138
+ mockUseOrganisations.mockReturnValue({ selectedOrganisation: { id: 'org-1' } });
139
+ mockGetPermissionMap.mockResolvedValue({});
140
+ mockIsPermittedCached.mockResolvedValue(true);
437
141
 
438
- mockUseOrganisations.mockReturnValue({
439
- selectedOrganisation: mockOrganisation
440
- });
142
+ const { result } = renderHook(() => useRBAC('dashboard'));
441
143
 
442
- // Mock RPC error
443
- mockSupabaseClient.rpc.mockRejectedValue(new Error('RPC Error'));
144
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
444
145
 
445
- const { result } = renderHook(() => useRBAC());
146
+ const allowed = await result.current.hasPermission('read', 'dashboard');
446
147
 
447
- await waitFor(() => {
448
- expect(result.current.error).toBeDefined();
449
- expect(result.current.isLoading).toBe(false);
450
- });
451
- });
148
+ expect(allowed).toBe(true);
149
+ expect(mockIsPermittedCached).toHaveBeenCalled();
150
+ });
452
151
 
453
- it('handles missing EventProvider gracefully', async () => {
454
- mockUseUnifiedAuth.mockReturnValue({
455
- user: mockUser,
456
- session: mockSession,
457
- supabase: mockSupabaseClient,
458
- appName: 'test-app'
459
- });
460
-
461
- mockUseOrganisations.mockReturnValue({
462
- selectedOrganisation: mockOrganisation
463
- });
464
-
465
- // Mock EventProvider throwing error
466
- mockUseEvents.mockImplementation(() => {
467
- throw new Error('EventProvider not available');
468
- });
469
-
470
- // Mock successful RPC calls
471
- mockSupabaseClient.rpc.mockImplementation((functionName: string) => {
472
- if (functionName === 'util_app_resolve') {
473
- return Promise.resolve({
474
- data: [{ app_id: 'test-app-id', has_access: true }],
475
- error: null
476
- });
477
- }
478
- if (functionName === 'rbac_permissions_get') {
479
- return Promise.resolve({
480
- data: [{ permission_type: 'all_permissions', role_name: 'super_admin' }],
481
- error: null
482
- });
483
- }
484
- return Promise.resolve({ data: null, error: null });
485
- });
486
-
487
- const { result } = renderHook(() => useRBAC());
488
-
489
- // Should not throw error and continue without event context
490
- expect(result.current.user).toEqual(mockUser);
491
-
492
- await waitFor(() => {
493
- expect(result.current.isLoading).toBe(false);
494
- });
152
+ it('handles denied app resolution securely', async () => {
153
+ mockUseUnifiedAuth.mockReturnValue({
154
+ user: { id: 'user-1' },
155
+ session: { access_token: 'token' },
156
+ appName: 'test-app',
495
157
  });
158
+ mockResolveAppContext.mockResolvedValue(null);
159
+
160
+ const { result } = renderHook(() => useRBAC());
161
+
162
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
163
+
164
+ expect(result.current.error).toBeInstanceOf(Error);
165
+ expect(result.current.globalRole).toBeNull();
496
166
  });
497
167
 
498
- describe('Hook Properties', () => {
499
- it('has all required properties', () => {
500
- const { result } = renderHook(() => useRBAC());
501
-
502
- expect(result.current).toHaveProperty('user');
503
- expect(result.current).toHaveProperty('globalRole');
504
- expect(result.current).toHaveProperty('organisationRole');
505
- expect(result.current).toHaveProperty('eventAppRole');
506
- expect(result.current).toHaveProperty('hasPermission');
507
- expect(result.current).toHaveProperty('hasGlobalPermission');
508
- expect(result.current).toHaveProperty('isSuperAdmin');
509
- expect(result.current).toHaveProperty('isOrgAdmin');
510
- expect(result.current).toHaveProperty('isEventAdmin');
511
- expect(result.current).toHaveProperty('canManageOrganisation');
512
- expect(result.current).toHaveProperty('canManageEvent');
513
- expect(result.current).toHaveProperty('isLoading');
514
- expect(result.current).toHaveProperty('error');
168
+ it('treats super admin role as having all permissions', async () => {
169
+ mockUseUnifiedAuth.mockReturnValue({
170
+ user: { id: 'user-1' },
171
+ session: { access_token: 'token' },
172
+ appName: 'test-app',
515
173
  });
516
-
517
- it('hasPermission is a function', () => {
518
- const { result } = renderHook(() => useRBAC());
519
-
520
- expect(typeof result.current.hasPermission).toBe('function');
521
- expect(typeof result.current.hasGlobalPermission).toBe('function');
174
+ mockGetRoleContext.mockResolvedValue({
175
+ globalRole: 'super_admin',
176
+ organisationRole: null,
177
+ eventAppRole: null,
522
178
  });
523
179
 
524
- it('computed properties work correctly', async () => {
525
- mockUseUnifiedAuth.mockReturnValue({
526
- user: mockUser,
527
- session: mockSession,
528
- supabase: mockSupabaseClient,
529
- appName: 'test-app'
530
- });
531
-
532
- mockUseOrganisations.mockReturnValue({
533
- selectedOrganisation: mockOrganisation
534
- });
535
-
536
- // Mock super admin RPC response
537
- mockSupabaseClient.rpc.mockImplementation((functionName: string) => {
538
- if (functionName === 'util_app_resolve') {
539
- return Promise.resolve({
540
- data: [{ app_id: 'test-app-id', has_access: true }],
541
- error: null
542
- });
543
- }
544
- if (functionName === 'rbac_permissions_get') {
545
- return Promise.resolve({
546
- data: [
547
- { permission_type: 'all_permissions', role_name: 'super_admin' },
548
- { permission_type: 'organisation_access', role_name: 'org_admin' },
549
- { permission_type: 'event_app_access', role_name: 'event_admin' }
550
- ],
551
- error: null
552
- });
553
- }
554
- return Promise.resolve({ data: null, error: null });
555
- });
556
-
557
- const { result } = renderHook(() => useRBAC());
558
-
559
- await waitFor(() => {
560
- expect(result.current.globalRole).toBe('super_admin');
561
- expect(result.current.organisationRole).toBe('org_admin');
562
- expect(result.current.eventAppRole).toBe('event_admin');
563
- });
564
-
565
- // Test computed properties
566
- expect(result.current.isSuperAdmin).toBe(true);
567
- expect(result.current.isOrgAdmin).toBe(true);
568
- expect(result.current.isEventAdmin).toBe(true);
569
- expect(result.current.canManageOrganisation).toBe(true);
570
- expect(result.current.canManageEvent).toBe(true);
571
- });
180
+ const { result } = renderHook(() => useRBAC());
181
+
182
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
183
+
184
+ expect(result.current.isSuperAdmin).toBe(true);
185
+ expect(result.current.hasGlobalPermission('any')).toBe(true);
572
186
  });
573
- });
187
+ });