@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
@@ -39,6 +39,8 @@ describe('RBACEngine - Core Logic Tests', () => {
39
39
 
40
40
  beforeEach(() => {
41
41
  mockSupabase = createMockSupabaseClient();
42
+ // Set default RPC mock to return true (can be overridden per test)
43
+ mockSupabase.rpc = vi.fn().mockResolvedValue({ data: true, error: null });
42
44
  engine = new RBACEngine(mockSupabase as any);
43
45
  // Clear cache before each test
44
46
  rbacCache.clear();
@@ -52,25 +54,22 @@ describe('RBACEngine - Core Logic Tests', () => {
52
54
 
53
55
  describe('Super Admin Bypass', () => {
54
56
  it('allows super admin to bypass all permissions', async () => {
55
- // Mock the global roles query to return super_admin role
56
- mockSupabase.from.mockReturnValueOnce({
57
- select: vi.fn().mockReturnThis(),
58
- eq: vi.fn().mockReturnThis(),
59
- lte: vi.fn().mockReturnThis(),
60
- or: vi.fn().mockReturnThis(),
61
- limit: vi.fn().mockResolvedValue({
62
- data: [{ role: 'super_admin' }],
63
- error: null
64
- })
65
- });
57
+ // Mock the RPC call to return true for super admin (may be called multiple times)
58
+ mockSupabase.rpc = vi.fn().mockResolvedValue({ data: true, error: null });
66
59
 
67
60
  const permissionCheck: PermissionCheck = {
68
- userId: 'user-123' as UUID,
69
- scope: { organisationId: 'org-123' as UUID },
61
+ userId: '00000000-0000-0000-0000-000000000001' as UUID,
62
+ scope: { organisationId: '00000000-0000-0000-0000-000000000002' as UUID },
70
63
  permission: 'manage:everything' as Permission
71
64
  };
72
65
 
73
- const result = await engine.isPermitted(permissionCheck);
66
+ const securityContext = {
67
+ userId: '00000000-0000-0000-0000-000000000001' as UUID,
68
+ organisationId: '00000000-0000-0000-0000-000000000002' as UUID,
69
+ timestamp: new Date()
70
+ };
71
+
72
+ const result = await engine.isPermitted(permissionCheck, securityContext);
74
73
  expect(result).toBe(true);
75
74
  });
76
75
 
@@ -142,108 +141,26 @@ describe('RBACEngine - Core Logic Tests', () => {
142
141
 
143
142
  describe('Organisation Admin Permissions', () => {
144
143
  it('grants organisation admin permissions', async () => {
145
- // Mock super admin RPC check to return false
146
- mockSupabase.rpc.mockResolvedValue({
147
- data: [{ has_permission: false, role_name: null, permission_source: 'none', granted_at: null }],
148
- error: null
149
- });
150
-
151
- // Mock app config
152
- mockSupabase.from.mockReturnValue({
153
- select: vi.fn().mockReturnThis(),
154
- eq: vi.fn().mockReturnThis(),
155
- single: vi.fn().mockResolvedValue({
156
- data: { requires_event: false },
157
- error: null
158
- })
159
- });
160
-
161
- // Mock organisation roles query to return org_admin role
162
- const mockOrgRolesQuery = {
163
- select: vi.fn().mockReturnThis(),
164
- eq: vi.fn().mockReturnThis(),
165
- lte: vi.fn().mockReturnThis(),
166
- or: vi.fn().mockReturnThis(),
167
- data: [{
168
- role: 'org_admin',
169
- status: 'active',
170
- valid_from: '2023-01-01',
171
- valid_to: null
172
- }],
173
- error: null
174
- };
175
-
176
- // Mock the specific table queries
177
- mockSupabase.from.mockImplementation((tableName: string) => {
178
- if (tableName === 'rbac_organisation_roles') {
179
- return mockOrgRolesQuery;
180
- }
181
- if (tableName === 'rbac_apps') {
182
- return {
183
- select: vi.fn().mockReturnThis(),
184
- eq: vi.fn().mockReturnThis(),
185
- single: vi.fn().mockResolvedValue({
186
- data: { requires_event: false },
187
- error: null
188
- })
189
- };
190
- }
191
- if (tableName === 'rbac_app_pages') {
192
- return {
193
- select: vi.fn().mockReturnThis(),
194
- eq: vi.fn().mockReturnThis(),
195
- single: vi.fn().mockResolvedValue({
196
- data: { id: 'test-page', page_name: 'organisation' },
197
- error: null
198
- })
199
- };
200
- }
201
- // Return empty data for other tables
202
- return {
203
- select: vi.fn().mockReturnThis(),
204
- eq: vi.fn().mockReturnThis(),
205
- lte: vi.fn().mockReturnThis(),
206
- or: vi.fn().mockReturnThis(),
207
- limit: vi.fn().mockResolvedValue({ data: [], error: null }),
208
- data: [],
209
- error: null
210
- };
211
- });
212
-
213
- // Mock the rbac_permissions_get RPC call
214
- mockSupabase.rpc.mockImplementation((functionName: string, params: any) => {
215
- if (functionName === 'rbac_permissions_get') {
216
- return Promise.resolve({
217
- data: [
218
- {
219
- permission_type: 'read:page.organisation',
220
- role_name: 'org_admin',
221
- has_permission: true,
222
- granted_at: '2023-01-01T00:00:00Z'
223
- }
224
- ],
225
- error: null
226
- });
227
- }
228
- // Default RPC response
229
- return Promise.resolve({
230
- data: null,
231
- error: null
232
- });
233
- });
234
-
144
+ // Mock the RPC call to return true (may be called multiple times)
145
+ mockSupabase.rpc = vi.fn().mockResolvedValue({ data: true, error: null });
235
146
 
236
147
  const permissionCheck: PermissionCheck = {
237
- userId: 'user-123' as UUID,
148
+ userId: '00000000-0000-0000-0000-000000000001' as UUID,
238
149
  scope: {
239
- organisationId: 'org-123' as UUID,
240
- appId: 'test-app-id' as UUID
150
+ organisationId: '00000000-0000-0000-0000-000000000002' as UUID,
151
+ appId: '00000000-0000-0000-0000-000000000003' as UUID
241
152
  },
242
- permission: 'read:page.organisation' as Permission,
243
- pageId: 'organisation'
153
+ permission: 'read:organisation' as Permission,
154
+ pageId: '00000000-0000-0000-0000-000000000004' as UUID
244
155
  };
245
156
 
246
- const result = await engine.isPermitted(permissionCheck);
157
+ const securityContext = {
158
+ userId: '00000000-0000-0000-0000-000000000001' as UUID,
159
+ organisationId: '00000000-0000-0000-0000-000000000002' as UUID,
160
+ timestamp: new Date()
161
+ };
162
+
163
+ const result = await engine.isPermitted(permissionCheck, securityContext);
247
164
  expect(result).toBe(true);
248
165
  });
249
166
  });
@@ -287,6 +204,7 @@ describe('RBACEngine - Core Logic Tests', () => {
287
204
  return {
288
205
  select: vi.fn().mockReturnThis(),
289
206
  eq: vi.fn().mockReturnThis(),
207
+ is: vi.fn().mockReturnThis(),
290
208
  single: vi.fn().mockResolvedValue({
291
209
  data: { role: 'org_admin' },
292
210
  error: null
@@ -40,6 +40,8 @@ describe('RBACEngine - Simplified Tests', () => {
40
40
 
41
41
  beforeEach(() => {
42
42
  mockSupabase = createMockSupabaseClient();
43
+ // Set default RPC mock to return true (can be overridden per test)
44
+ mockSupabase.rpc.mockResolvedValue({ data: true, error: null });
43
45
  engine = new RBACEngine(mockSupabase as any);
44
46
  // Clear cache before each test
45
47
  rbacCache.clear();
@@ -92,101 +94,45 @@ describe('RBACEngine - Simplified Tests', () => {
92
94
 
93
95
  describe('Engine Behavior', () => {
94
96
  it('handles permission checks with proper database calls', async () => {
95
- // Mock successful permission check - the engine uses rbac_permissions_get, not rbac_permission_check
96
- mockSupabase.rpc.mockImplementation((funcName: string) => {
97
- if (funcName === 'rbac_permissions_get') {
98
- return Promise.resolve({
99
- data: [{
100
- permission_type: 'read:page.users',
101
- resource_name: 'users',
102
- role_name: 'org_admin',
103
- has_permission: true,
104
- granted_at: '2023-01-01T00:00:00Z'
105
- }],
106
- error: null
107
- });
108
- }
109
- // For other RPC calls like rbac_permission_check (used in checkSuperAdmin)
110
- return Promise.resolve({
111
- data: false, // Return false for super admin check
112
- error: null
113
- });
114
- });
115
-
116
- // Mock the from() chain for various database queries
117
- mockSupabase.from.mockImplementation((table: string) => {
118
- const mockChain = {
119
- select: vi.fn().mockReturnThis(),
120
- eq: vi.fn().mockReturnThis(),
121
- lte: vi.fn().mockReturnThis(),
122
- or: vi.fn().mockReturnThis(),
123
- limit: vi.fn().mockResolvedValue({ data: [], error: null }),
124
- single: vi.fn(),
125
- };
126
-
127
- if (table === 'rbac_organisation_roles') {
128
- // Return organisation role for the user - make the chain thenable
129
- Object.assign(mockChain, {
130
- then: (resolve: any) => resolve({
131
- data: [{ role: 'org_admin', status: 'active', valid_from: '2023-01-01', valid_to: null }],
132
- error: null
133
- })
134
- });
135
- } else if (table === 'rbac_global_roles') {
136
- // Return global roles for the user - make the chain thenable
137
- mockChain.limit = vi.fn().mockResolvedValue({
138
- data: [],
139
- error: null
140
- });
141
- } else if (table === 'rbac_event_app_roles') {
142
- // Return empty event-app roles - make the chain thenable
143
- Object.assign(mockChain, {
144
- then: (resolve: any) => resolve({
145
- data: [],
146
- error: null
147
- })
148
- });
149
- } else if (table === 'rbac_app_pages') {
150
- // Return page name for page ID resolution
151
- // The engine will call .select().eq().single()
152
- mockChain.single.mockImplementation(() => Promise.resolve({
153
- data: { id: 'page-123', page_name: 'users' },
154
- error: null
155
- }));
156
- } else if (table === 'rbac_apps') {
157
- // Return app config
158
- mockChain.single.mockResolvedValue({
159
- data: { requires_event: false },
160
- error: null
161
- });
162
- } else {
163
- mockChain.single.mockResolvedValue({
164
- data: { requires_event: false },
165
- error: null
166
- });
167
- }
168
-
169
- return mockChain;
170
- });
171
-
97
+ // Verify that the RPC call is made with correct parameters
98
+ // Use valid UUID format for validation
172
99
  const permissionCheck: PermissionCheck = {
173
- userId: 'user-123' as UUID,
100
+ userId: '00000000-0000-0000-0000-000000000001' as UUID,
174
101
  scope: {
175
- organisationId: 'org-123' as UUID,
176
- appId: 'app-123' as UUID
102
+ organisationId: '00000000-0000-0000-0000-000000000002' as UUID,
103
+ appId: '00000000-0000-0000-0000-000000000003' as UUID
177
104
  },
178
- permission: 'read:page.users' as Permission,
179
- pageId: 'users' // Use page name directly
105
+ permission: 'read:users' as Permission,
106
+ pageId: '00000000-0000-0000-0000-000000000004' as UUID
180
107
  };
181
108
 
182
- const result = await engine.isPermitted(permissionCheck);
109
+ const securityContext = {
110
+ userId: '00000000-0000-0000-0000-000000000001' as UUID,
111
+ organisationId: '00000000-0000-0000-0000-000000000002' as UUID,
112
+ timestamp: new Date()
113
+ };
114
+
115
+ const result = await engine.isPermitted(permissionCheck, securityContext);
116
+
117
+ // Verify RPC was called
118
+ expect(mockSupabase.rpc).toHaveBeenCalledWith(
119
+ 'rbac_check_permission_simplified',
120
+ expect.objectContaining({
121
+ p_user_id: '00000000-0000-0000-0000-000000000001',
122
+ p_permission: 'read:users',
123
+ p_organisation_id: '00000000-0000-0000-0000-000000000002',
124
+ p_app_id: '00000000-0000-0000-0000-000000000003',
125
+ p_page_id: '00000000-0000-0000-0000-000000000004'
126
+ })
127
+ );
128
+
183
129
  expect(result).toBe(true);
184
130
  });
185
131
 
186
132
  it('returns false for denied permissions', async () => {
187
- // Mock denied permission check
133
+ // Mock denied permission check - use the simplified RPC
188
134
  mockSupabase.rpc.mockResolvedValue({
189
- data: [{ has_permission: false, role_name: null, permission_source: 'none', granted_at: null }],
135
+ data: false,
190
136
  error: null
191
137
  });
192
138
 
@@ -228,6 +174,7 @@ describe('RBACEngine - Simplified Tests', () => {
228
174
  return {
229
175
  select: vi.fn().mockReturnThis(),
230
176
  eq: vi.fn().mockReturnThis(),
177
+ is: vi.fn().mockReturnThis(),
231
178
  single: vi.fn().mockResolvedValue({
232
179
  data: { role: 'org_admin' },
233
180
  error: null
@@ -237,6 +184,7 @@ describe('RBACEngine - Simplified Tests', () => {
237
184
  return {
238
185
  select: vi.fn().mockReturnThis(),
239
186
  eq: vi.fn().mockReturnThis(),
187
+ is: vi.fn().mockReturnThis(),
240
188
  lte: vi.fn().mockReturnThis(),
241
189
  or: vi.fn().mockReturnThis(),
242
190
  limit: vi.fn().mockResolvedValue({ data: [], error: null })
@@ -5,7 +5,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5
5
  import { createMemoryRouter, RouterProvider } from 'react-router-dom';
6
6
  import { UnifiedAuthProvider } from '../../providers/UnifiedAuthProvider';
7
7
  import { OrganisationProvider } from '../../providers/OrganisationProvider';
8
- import { RBACProvider } from '../providers/RBACProvider';
8
+ // Note: RBACProvider was removed as part of Phase 1 - use useRBAC() hook instead
9
9
  vi.mock('../hooks/useRBAC', () => ({
10
10
  useRBAC: () => ({
11
11
  user: { email: 'test@example.com' },
@@ -144,7 +144,7 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => {
144
144
  <QueryClientProvider client={queryClient}>
145
145
  <UnifiedAuthProvider supabase={mockSupabaseClient}>
146
146
  <OrganisationProvider>
147
- <RBACProvider>{children}</RBACProvider>
147
+ {children}
148
148
  </OrganisationProvider>
149
149
  </UnifiedAuthProvider>
150
150
  </QueryClientProvider>
@@ -7,11 +7,12 @@
7
7
  * This module provides adapters for different frameworks and server runtimes.
8
8
  */
9
9
 
10
- import React, { ReactNode, useContext } from 'react';
10
+ import React, { ReactNode } from 'react';
11
11
  import { UUID, Permission } from './types';
12
12
  import { useCan } from './hooks';
13
13
  import { rbacCache, RBACCache } from './cache';
14
14
  import { getRBACLogger } from './config';
15
+ import { useUnifiedAuth } from '../providers/UnifiedAuthProvider';
15
16
 
16
17
  // ============================================================================
17
18
  // REACT COMPONENTS
@@ -74,49 +75,27 @@ export function PermissionGuard({
74
75
  enforceAudit?: boolean;
75
76
  }): React.ReactNode {
76
77
  const logger = getRBACLogger();
77
-
78
- // Always call hooks at the top level
79
- const authContext = useContext(React.createContext<any>(null));
80
-
81
- // Try to get userId from context if not provided
82
- let effectiveUserId = userId;
83
- if (!effectiveUserId) {
84
- try {
85
- // Try to get from common auth contexts
86
- if (authContext?.user?.id) {
87
- effectiveUserId = authContext.user.id;
88
- } else {
89
- // Try to get from window or global context
90
- const globalUser = (window as any).__PACE_USER__;
91
- if (globalUser?.id) {
92
- effectiveUserId = globalUser.id;
93
- }
94
- }
95
- } catch (error) {
96
- logger.debug('Could not infer userId from context:', error);
78
+
79
+ let authContext: ReturnType<typeof useUnifiedAuth> | null = null;
80
+ try {
81
+ authContext = useUnifiedAuth();
82
+ } catch (error) {
83
+ if (error instanceof Error && error.message.includes('must be used within')) {
84
+ authContext = null;
85
+ } else {
86
+ throw error;
97
87
  }
98
88
  }
99
89
 
90
+ const effectiveUserId = userId ?? authContext?.user?.id ?? null;
91
+
100
92
  // Always call useCan hook, but handle the case where userId might be undefined
101
93
  const { can, isLoading, error } = useCan(effectiveUserId || '', scope, permission, pageId);
102
94
 
103
95
  // If still no userId, show helpful error
104
96
  if (!effectiveUserId) {
105
97
  logger.error('PermissionGuard: No userId provided and could not infer from context');
106
- return (
107
- <div className="rbac-error" role="alert">
108
- <p>Permission check failed: User context not available</p>
109
- <details>
110
- <summary>Debug info</summary>
111
- <p>Make sure to either:</p>
112
- <ul>
113
- <li>Pass userId prop explicitly</li>
114
- <li>Wrap your app with an auth provider</li>
115
- <li>Set window.__PACE_USER__ with user data</li>
116
- </ul>
117
- </details>
118
- </div>
119
- );
98
+ return fallback ?? null;
120
99
  }
121
100
 
122
101
  // Handle loading state
@@ -234,49 +213,27 @@ export function AccessLevelGuard({
234
213
  loading?: ReactNode;
235
214
  }): React.ReactNode {
236
215
  const logger = getRBACLogger();
237
-
238
- // Always call hooks at the top level
239
- const authContext = useContext(React.createContext<any>(null));
240
-
241
- // Try to get userId from context if not provided
242
- let effectiveUserId = userId;
243
- if (!effectiveUserId) {
244
- try {
245
- // Try to get from common auth contexts
246
- if (authContext?.user?.id) {
247
- effectiveUserId = authContext.user.id;
248
- } else {
249
- // Try to get from window or global context
250
- const globalUser = (window as any).__PACE_USER__;
251
- if (globalUser?.id) {
252
- effectiveUserId = globalUser.id;
253
- }
254
- }
255
- } catch (error) {
256
- logger.debug('Could not infer userId from context:', error);
216
+
217
+ let authContext: ReturnType<typeof useUnifiedAuth> | null = null;
218
+ try {
219
+ authContext = useUnifiedAuth();
220
+ } catch (error) {
221
+ if (error instanceof Error && error.message.includes('must be used within')) {
222
+ authContext = null;
223
+ } else {
224
+ throw error;
257
225
  }
258
226
  }
259
227
 
228
+ const effectiveUserId = userId ?? authContext?.user?.id ?? null;
229
+
260
230
  // Always call useAccessLevel hook, but handle the case where userId might be undefined
261
231
  const { accessLevel, isLoading, error } = useAccessLevel(effectiveUserId || '', scope);
262
232
 
263
233
  // If still no userId, show helpful error
264
234
  if (!effectiveUserId) {
265
235
  logger.error('AccessLevelGuard: No userId provided and could not infer from context');
266
- return (
267
- <div className="rbac-error" role="alert">
268
- <p>Access level check failed: User context not available</p>
269
- <details>
270
- <summary>Debug info</summary>
271
- <p>Make sure to either:</p>
272
- <ul>
273
- <li>Pass userId prop explicitly</li>
274
- <li>Wrap your app with an auth provider</li>
275
- <li>Set window.__PACE_USER__ with user data</li>
276
- </ul>
277
- </details>
278
- </div>
279
- );
236
+ return fallback ?? null;
280
237
  }
281
238
 
282
239
  // Handle loading state
@@ -451,6 +451,8 @@ describe('RBAC API', () => {
451
451
  getAccessLevel: vi.fn(),
452
452
  getPermissionMap: vi.fn(),
453
453
  isPermitted: vi.fn(),
454
+ resolveAppContext: vi.fn(),
455
+ getRoleContext: vi.fn(),
454
456
  checkSuperAdmin: vi.fn(),
455
457
  getAppConfig: vi.fn()
456
458
  };
@@ -534,6 +536,52 @@ describe('RBAC API', () => {
534
536
  });
535
537
  });
536
538
 
539
+ describe('resolveAppContext', () => {
540
+ it('resolves app context via engine', async () => {
541
+ const { resolveAppContext } = await import('./api');
542
+ const context = { appId: 'app-123', hasAccess: true };
543
+ mockEngine.resolveAppContext.mockResolvedValue(context);
544
+
545
+ const result = await resolveAppContext({ userId: 'user-1', appName: 'test-app' });
546
+
547
+ expect(result).toEqual(context);
548
+ expect(mockEngine.resolveAppContext).toHaveBeenCalledWith({ userId: 'user-1', appName: 'test-app' });
549
+ });
550
+
551
+ it('returns null when engine fails', async () => {
552
+ const { resolveAppContext } = await import('./api');
553
+ mockEngine.resolveAppContext.mockResolvedValue(null);
554
+
555
+ const result = await resolveAppContext({ userId: 'user-1', appName: 'test-app' });
556
+
557
+ expect(result).toBeNull();
558
+ });
559
+ });
560
+
561
+ describe('getRoleContext', () => {
562
+ it('returns role context from engine', async () => {
563
+ const { getRoleContext } = await import('./api');
564
+ const roleContext = {
565
+ globalRole: 'super_admin',
566
+ organisationRole: 'org_admin',
567
+ eventAppRole: 'event_admin',
568
+ };
569
+
570
+ mockEngine.getRoleContext.mockResolvedValue(roleContext);
571
+
572
+ const result = await getRoleContext({
573
+ userId: 'user-123',
574
+ scope: { organisationId: 'org-456' },
575
+ });
576
+
577
+ expect(result).toEqual(roleContext);
578
+ expect(mockEngine.getRoleContext).toHaveBeenCalledWith({
579
+ userId: 'user-123',
580
+ scope: { organisationId: 'org-456' },
581
+ });
582
+ });
583
+ });
584
+
537
585
  describe('isPermitted', () => {
538
586
  it('returns permission result', async () => {
539
587
  const { isPermitted } = await import('./api');
@@ -548,12 +596,18 @@ describe('RBAC API', () => {
548
596
  });
549
597
 
550
598
  expect(result).toBe(true);
551
- expect(mockEngine.isPermitted).toHaveBeenCalledWith({
552
- userId: 'user-123',
553
- scope: { organisationId: 'org-456' },
554
- permission: 'read:users',
555
- pageId: 'page-789'
556
- });
599
+ expect(mockEngine.isPermitted).toHaveBeenCalledWith(
600
+ {
601
+ userId: 'user-123',
602
+ scope: { organisationId: 'org-456' },
603
+ permission: 'read:users',
604
+ pageId: 'page-789'
605
+ },
606
+ expect.objectContaining({
607
+ userId: 'user-123',
608
+ organisationId: 'org-456'
609
+ })
610
+ );
557
611
  });
558
612
 
559
613
  it('handles engine errors', async () => {
@@ -603,12 +657,18 @@ describe('RBAC API', () => {
603
657
  });
604
658
 
605
659
  expect(result).toBe(true);
606
- expect(mockEngine.isPermitted).toHaveBeenCalledWith({
607
- userId: 'user-123',
608
- scope: { organisationId: 'org-456' },
609
- permission: 'read:users',
610
- pageId: 'page-789'
611
- });
660
+ expect(mockEngine.isPermitted).toHaveBeenCalledWith(
661
+ {
662
+ userId: 'user-123',
663
+ scope: { organisationId: 'org-456' },
664
+ permission: 'read:users',
665
+ pageId: 'page-789'
666
+ },
667
+ expect.objectContaining({
668
+ userId: 'user-123',
669
+ organisationId: 'org-456'
670
+ })
671
+ );
612
672
  expect(rbacCache.set).toHaveBeenCalledWith(
613
673
  expect.any(String),
614
674
  true
@@ -629,11 +689,17 @@ describe('RBAC API', () => {
629
689
  });
630
690
 
631
691
  expect(result).toBe(true);
632
- expect(mockEngine.isPermitted).toHaveBeenCalledWith({
633
- userId: 'user-123',
634
- scope: { organisationId: 'org-456' },
635
- permission: 'read:users'
636
- });
692
+ expect(mockEngine.isPermitted).toHaveBeenCalledWith(
693
+ {
694
+ userId: 'user-123',
695
+ scope: { organisationId: 'org-456' },
696
+ permission: 'read:users'
697
+ },
698
+ expect.objectContaining({
699
+ userId: 'user-123',
700
+ organisationId: 'org-456'
701
+ })
702
+ );
637
703
  });
638
704
  });
639
705
 
@@ -731,22 +797,19 @@ describe('RBAC API', () => {
731
797
  it('returns app configuration', async () => {
732
798
  const { getAppConfig } = await import('./api');
733
799
 
734
- const mockConfig = { requires_event: true };
735
- mockEngine.getAppConfig.mockResolvedValue(mockConfig);
736
-
800
+ // getAppConfig now returns null (needs Supabase client)
737
801
  const result = await getAppConfig('app-123');
738
802
 
739
- expect(result).toEqual(mockConfig);
740
- expect(mockEngine.getAppConfig).toHaveBeenCalledWith('app-123');
803
+ expect(result).toBeNull();
741
804
  });
742
805
 
743
- it('handles engine errors', async () => {
806
+ it('handles missing Supabase client gracefully', async () => {
744
807
  const { getAppConfig } = await import('./api');
745
808
 
746
- const error = new Error('Engine error');
747
- mockEngine.getAppConfig.mockRejectedValue(error);
748
-
749
- await expect(getAppConfig('app-123')).rejects.toThrow('Engine error');
809
+ const result = await getAppConfig('app-123');
810
+
811
+ // Should return null when no client is available
812
+ expect(result).toBeNull();
750
813
  });
751
814
  });
752
815