@jmruthers/pace-core 0.5.76 → 0.5.77

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 (446) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-SBHZQtCH.d.ts} +5 -118
  3. package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
  4. package/dist/{DataTable-4GAVPIEG.js → DataTable-QCNCV6IK.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-Z2FWNW7O.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-7PX43UYN.js} +196 -626
  11. package/dist/chunk-7PX43UYN.js.map +1 -0
  12. package/dist/{chunk-Y6TXWPJO.js → chunk-C4RQ3GQA.js} +105 -31
  13. package/dist/chunk-C4RQ3GQA.js.map +1 -0
  14. package/dist/{chunk-LW7MMEAQ.js → chunk-CRKP3HXI.js} +2 -2
  15. package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
  16. package/dist/chunk-CVMVPYAL.js.map +1 -0
  17. package/dist/{chunk-M5IWZRBT.js → chunk-DDPG7FCX.js} +2187 -981
  18. package/dist/chunk-DDPG7FCX.js.map +1 -0
  19. package/dist/{chunk-URUTVZ7N.js → chunk-DVHZ5L55.js} +2 -2
  20. package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
  21. package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
  22. package/dist/{chunk-WN6XJWOS.js → chunk-JDQ7T3QB.js} +256 -743
  23. package/dist/chunk-JDQ7T3QB.js.map +1 -0
  24. package/dist/{chunk-KK73ZB4E.js → chunk-LMYTEMUH.js} +153 -132
  25. package/dist/chunk-LMYTEMUH.js.map +1 -0
  26. package/dist/{chunk-AFGTSUAD.js → chunk-NKT2DLZI.js} +4 -4
  27. package/dist/{chunk-KHJS6VIA.js → chunk-PUKTJMRT.js} +157 -112
  28. package/dist/chunk-PUKTJMRT.js.map +1 -0
  29. package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
  30. package/dist/chunk-Q7APDV6H.js.map +1 -0
  31. package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
  32. package/dist/chunk-S63MFSY6.js.map +1 -0
  33. package/dist/{chunk-NTNILOBC.js → chunk-TLD5BEU6.js} +4 -4
  34. package/dist/chunk-WUXCWRL6.js +20 -0
  35. package/dist/chunk-WUXCWRL6.js.map +1 -0
  36. package/dist/{chunk-A4FUBC7B.js → chunk-Z3T6RK3K.js} +2 -4
  37. package/dist/{chunk-A4FUBC7B.js.map → chunk-Z3T6RK3K.js.map} +1 -1
  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 +6 -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 +6 -0
  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 +26 -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/styles/base.css +208 -0
  373. package/src/styles/semantic.css +24 -0
  374. package/src/types/database.generated.ts +7347 -0
  375. package/src/types/database.ts +20 -0
  376. package/src/utils/logger.ts +179 -0
  377. package/src/utils/organisationContext.ts +11 -4
  378. package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
  379. package/dist/appNameResolver-UURKN7NF.js +0 -22
  380. package/dist/audit-6TOCAMKO.js.map +0 -1
  381. package/dist/chunk-B2WTCLCV.js.map +0 -1
  382. package/dist/chunk-FGMFQSHX.js.map +0 -1
  383. package/dist/chunk-K34IM5CT.js.map +0 -1
  384. package/dist/chunk-KHJS6VIA.js.map +0 -1
  385. package/dist/chunk-KK73ZB4E.js.map +0 -1
  386. package/dist/chunk-M5IWZRBT.js.map +0 -1
  387. package/dist/chunk-ULBI5JGB.js +0 -109
  388. package/dist/chunk-ULBI5JGB.js.map +0 -1
  389. package/dist/chunk-WN6XJWOS.js.map +0 -1
  390. package/dist/chunk-XLZ7U46Z.js.map +0 -1
  391. package/dist/chunk-Y6TXWPJO.js.map +0 -1
  392. package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
  393. package/docs/TERMINOLOGY.md +0 -231
  394. package/docs/api/interfaces/RBACContextType.md +0 -468
  395. package/docs/api/interfaces/RBACProviderProps.md +0 -107
  396. package/docs/best-practices/performance-expansion.md +0 -473
  397. package/docs/breaking-changes.md +0 -179
  398. package/docs/consuming-app-example.md +0 -290
  399. package/docs/documentation-templates.md +0 -539
  400. package/docs/examples/navigation-menu-auth-fix.md +0 -344
  401. package/docs/getting-started/examples/basic-auth-app.md +0 -520
  402. package/docs/getting-started/examples/full-featured-app.md +0 -616
  403. package/docs/getting-started/quick-start.md +0 -376
  404. package/docs/implementation-guides/datatable-filtering.md +0 -313
  405. package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
  406. package/docs/implementation-guides/hierarchical-datatable.md +0 -850
  407. package/docs/implementation-guides/large-datasets.md +0 -281
  408. package/docs/implementation-guides/performance.md +0 -403
  409. package/docs/migration/quick-migration-guide.md +0 -320
  410. package/docs/migration-guide.md +0 -193
  411. package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
  412. package/docs/performance/README.md +0 -551
  413. package/docs/style-guide.md +0 -964
  414. package/docs/troubleshooting/authentication-issues.md +0 -334
  415. package/docs/troubleshooting/debugging.md +0 -1117
  416. package/docs/troubleshooting/migration.md +0 -918
  417. package/src/__tests__/hooks/usePermissions.test.ts +0 -261
  418. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
  419. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
  420. package/src/hooks/services/__tests__/useServiceHooks.test.tsx +0 -137
  421. package/src/hooks/services/usePermissions.ts +0 -70
  422. package/src/hooks/services/useRBACService.ts +0 -30
  423. package/src/hooks/usePermissionCheck.ts +0 -150
  424. package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
  425. package/src/providers/services/RBACServiceProvider.tsx +0 -79
  426. package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
  427. package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
  428. package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
  429. package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
  430. package/src/rbac/providers/RBACProvider.tsx +0 -645
  431. package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
  432. package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
  433. package/src/rbac/providers/index.ts +0 -11
  434. package/src/services/RBACService.ts +0 -522
  435. package/src/services/__tests__/RBACService.test.ts +0 -492
  436. package/src/services/interfaces/IRBACService.ts +0 -62
  437. package/src/utils/appNameResolver.test 2.ts +0 -494
  438. /package/dist/{DataTable-4GAVPIEG.js.map → DataTable-QCNCV6IK.js.map} +0 -0
  439. /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-Z2FWNW7O.js.map} +0 -0
  440. /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
  441. /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
  442. /package/dist/{chunk-LW7MMEAQ.js.map → chunk-CRKP3HXI.js.map} +0 -0
  443. /package/dist/{chunk-URUTVZ7N.js.map → chunk-DVHZ5L55.js.map} +0 -0
  444. /package/dist/{chunk-AFGTSUAD.js.map → chunk-NKT2DLZI.js.map} +0 -0
  445. /package/dist/{chunk-NTNILOBC.js.map → chunk-TLD5BEU6.js.map} +0 -0
  446. /package/docs/{app.css.example → styles/app.css.example} +0 -0
@@ -1,164 +1,180 @@
1
1
  import {
2
2
  getAccessLevel,
3
3
  getPermissionMap,
4
+ getRBACLogger,
5
+ getRoleContext,
4
6
  isPermitted,
5
- isPermittedCached
6
- } from "./chunk-FGMFQSHX.js";
7
+ isPermittedCached,
8
+ resolveAppContext
9
+ } from "./chunk-S63MFSY6.js";
7
10
  import {
8
11
  init_useOrganisations,
9
12
  useEvents,
10
13
  useOrganisations
11
- } from "./chunk-LW7MMEAQ.js";
14
+ } from "./chunk-CRKP3HXI.js";
12
15
  import {
13
16
  init_UnifiedAuthProvider,
14
17
  useUnifiedAuth
15
- } from "./chunk-WN6XJWOS.js";
18
+ } from "./chunk-JDQ7T3QB.js";
16
19
  import {
17
- getCurrentAppName,
18
- init_appNameResolver
19
- } from "./chunk-5BSLGBYI.js";
20
+ getCurrentAppName
21
+ } from "./chunk-JCQZ6LA7.js";
20
22
 
21
23
  // src/rbac/hooks/useRBAC.ts
22
24
  init_UnifiedAuthProvider();
23
25
  init_useOrganisations();
24
26
  import { useState, useEffect, useCallback, useMemo } from "react";
27
+ function mapAccessLevelToEventRole(level) {
28
+ switch (level) {
29
+ case "viewer":
30
+ return "viewer";
31
+ case "participant":
32
+ return "participant";
33
+ case "planner":
34
+ return "planner";
35
+ case "admin":
36
+ case "super":
37
+ return "event_admin";
38
+ default:
39
+ return null;
40
+ }
41
+ }
25
42
  function useRBAC(pageId) {
26
- const { user, session, supabase, appName } = useUnifiedAuth();
43
+ const logger = getRBACLogger();
44
+ const { user, session, appName } = useUnifiedAuth();
27
45
  const { selectedOrganisation } = useOrganisations();
28
46
  let selectedEvent = null;
29
47
  try {
30
48
  const eventsContext = useEvents();
31
49
  selectedEvent = eventsContext.selectedEvent;
32
50
  } catch (error2) {
33
- console.debug("useRBAC: EventProvider not available, continuing without event context");
51
+ logger.debug("[useRBAC] Event provider not available, continuing without event context", error2);
34
52
  }
35
53
  const [globalRole, setGlobalRole] = useState(null);
36
54
  const [organisationRole, setOrganisationRole] = useState(null);
37
55
  const [eventAppRole, setEventAppRole] = useState(null);
38
- const [permissions, setPermissions] = useState([]);
56
+ const [permissionMap, setPermissionMap] = useState({});
57
+ const [currentScope, setCurrentScope] = useState(null);
39
58
  const [isLoading, setIsLoading] = useState(false);
40
59
  const [error, setError] = useState(null);
60
+ const resetState = useCallback(() => {
61
+ setGlobalRole(null);
62
+ setOrganisationRole(null);
63
+ setEventAppRole(null);
64
+ setPermissionMap({});
65
+ setCurrentScope(null);
66
+ }, []);
41
67
  const loadRBACContext = useCallback(async () => {
42
- if (!user || !session || !supabase || !appName) {
43
- console.log("[useRBAC] Missing required dependencies, clearing RBAC state");
44
- setGlobalRole(null);
45
- setOrganisationRole(null);
46
- setEventAppRole(null);
47
- setPermissions([]);
68
+ if (!user || !session) {
69
+ resetState();
48
70
  return;
49
71
  }
50
72
  setIsLoading(true);
51
73
  setError(null);
52
74
  try {
53
- const { data: appData, error: appError } = await supabase.rpc("util_app_resolve", {
54
- p_app_name: appName,
55
- p_user_id: user.id
56
- });
57
- if (appError || !appData || appData.length === 0) {
58
- console.warn("App not found or inactive:", appName);
59
- setIsLoading(false);
60
- return;
61
- }
62
- const app = appData[0];
63
- if (!app.has_access) {
64
- console.warn("User does not have access to app:", appName);
65
- setIsLoading(false);
66
- return;
67
- }
68
- const { data, error: rpcError } = await supabase.rpc("rbac_permissions_get", {
69
- p_user_id: user.id,
70
- p_app_id: app.app_id,
71
- p_event_id: selectedEvent?.event_id || null,
72
- p_organisation_id: selectedOrganisation?.id || null
73
- });
74
- if (rpcError) {
75
- throw new Error(`Failed to load RBAC permissions: ${rpcError.message}`);
76
- }
77
- if (data) {
78
- const globalPerms = data.filter((p) => p.permission_type === "all_permissions");
79
- const orgPerms = data.filter((p) => p.permission_type === "organisation_access");
80
- const eventPerms = data.filter((p) => p.permission_type === "event_app_access");
81
- setGlobalRole(globalPerms.length > 0 ? "super_admin" : null);
82
- setOrganisationRole(orgPerms.length > 0 ? orgPerms[0].role_name : null);
83
- setEventAppRole(eventPerms.length > 0 ? eventPerms[0].role_name : null);
84
- setPermissions(data);
85
- } else {
86
- setPermissions([]);
75
+ let appId;
76
+ if (appName) {
77
+ const resolved = await resolveAppContext({ userId: user.id, appName });
78
+ if (!resolved || !resolved.hasAccess) {
79
+ throw new Error(`User does not have access to app "${appName}"`);
80
+ }
81
+ appId = resolved.appId;
87
82
  }
83
+ const scope = {
84
+ organisationId: selectedOrganisation?.id,
85
+ eventId: selectedEvent?.event_id || void 0,
86
+ appId
87
+ };
88
+ setCurrentScope(scope);
89
+ const [map, roleContext, accessLevel] = await Promise.all([
90
+ getPermissionMap({ userId: user.id, scope }),
91
+ getRoleContext({ userId: user.id, scope }),
92
+ getAccessLevel({ userId: user.id, scope })
93
+ ]);
94
+ setPermissionMap(map);
95
+ setGlobalRole(roleContext.globalRole);
96
+ setOrganisationRole(roleContext.organisationRole);
97
+ setEventAppRole(roleContext.eventAppRole || mapAccessLevelToEventRole(accessLevel));
88
98
  } catch (err) {
89
- console.error("RBAC Context Loading Error:", err);
90
- setError(err instanceof Error ? err : new Error("Unknown error loading RBAC context"));
91
- setPermissions([]);
99
+ const handledError = err instanceof Error ? err : new Error("Failed to load RBAC context");
100
+ logger.error("[useRBAC] Error loading RBAC context:", handledError);
101
+ setError(handledError);
102
+ resetState();
92
103
  } finally {
93
104
  setIsLoading(false);
94
105
  }
95
- }, [user?.id, session, supabase, appName, selectedEvent?.event_id, selectedOrganisation?.id, pageId]);
96
- const hasPermission = useCallback(async (operation, targetPageId) => {
97
- if (!user || !session || !supabase || !appName) return false;
98
- if (globalRole === "super_admin") {
99
- return true;
100
- }
101
- try {
102
- const { data: appData, error: appError } = await supabase.rpc("util_app_resolve", {
103
- p_app_name: appName,
104
- p_user_id: user.id
105
- });
106
- if (appError || !appData || appData.length === 0) {
107
- console.warn("App not found or inactive:", appName);
106
+ }, [appName, logger, resetState, selectedEvent?.event_id, selectedOrganisation?.id, session, user]);
107
+ const hasPermission = useCallback(
108
+ async (operationOrPermission, targetPageId) => {
109
+ if (!user) {
110
+ logger.warn("[useRBAC] Permission check attempted without authenticated user context");
108
111
  return false;
109
112
  }
110
- const app = appData[0];
111
- if (!app.has_access) {
112
- console.warn("User does not have access to app:", appName);
113
+ if (!currentScope || !currentScope.organisationId) {
114
+ logger.error("[useRBAC] Permission check denied due to missing organisation context", {
115
+ hasScope: !!currentScope,
116
+ scope: currentScope,
117
+ userId: user.id,
118
+ permission: operationOrPermission
119
+ });
113
120
  return false;
114
121
  }
115
- const { data, error: error2 } = await supabase.rpc("rbac_page_access_check", {
116
- p_user_id: user.id,
117
- p_app_id: app.app_id,
118
- p_page_id: targetPageId || pageId || "default",
119
- p_operation: operation,
120
- p_event_id: selectedEvent?.event_id,
121
- p_organisation_id: selectedOrganisation?.id
122
- });
123
- if (error2) {
124
- console.error("Permission check failed:", error2);
122
+ if (globalRole === "super_admin" || permissionMap["*"]) {
123
+ logger.info("[useRBAC] Super admin bypass granted", {
124
+ userId: user.id,
125
+ permission: operationOrPermission
126
+ });
127
+ return true;
128
+ }
129
+ let permission;
130
+ if (operationOrPermission.includes(":")) {
131
+ permission = operationOrPermission;
132
+ } else if (targetPageId || pageId) {
133
+ permission = `${operationOrPermission}:${targetPageId || pageId}`;
134
+ } else {
135
+ permission = operationOrPermission;
136
+ }
137
+ const cachedValue = permissionMap[permission];
138
+ if (cachedValue === true) {
139
+ return true;
140
+ }
141
+ if (cachedValue === false) {
125
142
  return false;
126
143
  }
127
- return data === true;
128
- } catch (err) {
129
- console.error("Permission check error:", err);
130
- return false;
131
- }
132
- }, [user, supabase, appName, selectedEvent, selectedOrganisation, pageId, globalRole]);
133
- const hasGlobalPermission = useCallback((permission) => {
134
- if (globalRole === "super_admin") {
135
- return true;
136
- }
137
- if (permission === "super_admin") {
138
- return globalRole === "super_admin";
139
- }
140
- if (permission === "org_admin") {
141
- return organisationRole === "org_admin" || globalRole === "super_admin";
142
- }
143
- return false;
144
- }, [globalRole, organisationRole]);
145
- const isSuperAdmin = useMemo(() => globalRole === "super_admin", [globalRole]);
146
- const isOrgAdmin = useMemo(() => organisationRole === "org_admin", [organisationRole]);
147
- const isEventAdmin = useMemo(() => eventAppRole === "event_admin", [eventAppRole]);
148
- const canManageOrganisation = useMemo(
149
- () => isSuperAdmin || isOrgAdmin,
150
- [isSuperAdmin, isOrgAdmin]
144
+ return isPermittedCached({
145
+ userId: user.id,
146
+ scope: currentScope,
147
+ permission,
148
+ pageId: targetPageId ?? pageId
149
+ });
150
+ },
151
+ [currentScope, globalRole, logger, pageId, permissionMap, user]
151
152
  );
152
- const canManageEvent = useMemo(
153
- () => isSuperAdmin || isEventAdmin,
154
- [isSuperAdmin, isEventAdmin]
153
+ const hasGlobalPermission = useCallback(
154
+ (permission) => {
155
+ if (globalRole === "super_admin" || permissionMap["*"]) {
156
+ return true;
157
+ }
158
+ if (permission === "super_admin") {
159
+ return globalRole === "super_admin";
160
+ }
161
+ if (permission === "org_admin") {
162
+ return organisationRole === "org_admin";
163
+ }
164
+ return permissionMap[permission] === true;
165
+ },
166
+ [globalRole, organisationRole, permissionMap]
155
167
  );
168
+ const isSuperAdmin = useMemo(() => globalRole === "super_admin" || permissionMap["*"] === true, [globalRole, permissionMap]);
169
+ const isOrgAdmin = useMemo(() => organisationRole === "org_admin" || isSuperAdmin, [organisationRole, isSuperAdmin]);
170
+ const isEventAdmin = useMemo(() => eventAppRole === "event_admin" || isSuperAdmin, [eventAppRole, isSuperAdmin]);
171
+ const canManageOrganisation = useMemo(() => isSuperAdmin || organisationRole === "org_admin", [isSuperAdmin, organisationRole]);
172
+ const canManageEvent = useMemo(() => isSuperAdmin || eventAppRole === "event_admin", [isSuperAdmin, eventAppRole]);
156
173
  useEffect(() => {
157
174
  loadRBACContext();
158
175
  }, [loadRBACContext]);
159
176
  return {
160
177
  user,
161
- // Add user to the returned context
162
178
  globalRole,
163
179
  organisationRole,
164
180
  eventAppRole,
@@ -198,7 +214,6 @@ async function createScopeFromEvent(supabase, eventId, appId) {
198
214
  }
199
215
 
200
216
  // src/rbac/hooks/useResolvedScope.ts
201
- init_appNameResolver();
202
217
  function useResolvedScope({
203
218
  supabase,
204
219
  selectedOrganisationId,
@@ -256,7 +271,14 @@ function useResolvedScope({
256
271
  }
257
272
  }
258
273
  }
274
+ console.log("[useResolvedScope] Attempting to resolve scope:", {
275
+ selectedOrganisationId,
276
+ selectedEventId,
277
+ appId: appId || "NOT RESOLVED YET",
278
+ resolveStep: "initial"
279
+ });
259
280
  if (selectedOrganisationId && selectedEventId) {
281
+ console.log("[useResolvedScope] Resolving with both org and event");
260
282
  if (!cancelled) {
261
283
  setResolvedScope({
262
284
  organisationId: selectedOrganisationId,
@@ -268,6 +290,7 @@ function useResolvedScope({
268
290
  return;
269
291
  }
270
292
  if (selectedOrganisationId) {
293
+ console.log("[useResolvedScope] Resolving with organisation only");
271
294
  if (!cancelled) {
272
295
  setResolvedScope({
273
296
  organisationId: selectedOrganisationId,
@@ -280,6 +303,7 @@ function useResolvedScope({
280
303
  }
281
304
  if (selectedEventId && supabase) {
282
305
  try {
306
+ console.log("[useResolvedScope] Resolving from event:", { selectedEventId, appId });
283
307
  const eventScope = await createScopeFromEvent(supabase, selectedEventId, appId);
284
308
  if (!eventScope) {
285
309
  console.error("[useResolvedScope] Could not resolve organization from event context");
@@ -290,6 +314,7 @@ function useResolvedScope({
290
314
  }
291
315
  return;
292
316
  }
317
+ console.log("[useResolvedScope] Resolved from event:", eventScope);
293
318
  if (!cancelled) {
294
319
  setResolvedScope({
295
320
  ...eventScope,
@@ -371,13 +396,22 @@ function usePermissions(userId, scope) {
371
396
  fetchPermissions();
372
397
  }, [userId, scope.organisationId, scope.eventId, scope.appId]);
373
398
  const hasPermission = useCallback2((permission) => {
374
- return !!permissions[permission];
399
+ if (permissions["*"]) {
400
+ return true;
401
+ }
402
+ return permissions[permission] === true;
375
403
  }, [permissions]);
376
404
  const hasAnyPermission = useCallback2((permissionList) => {
377
- return permissionList.some((p) => !!permissions[p]);
405
+ if (permissions["*"]) {
406
+ return true;
407
+ }
408
+ return permissionList.some((p) => permissions[p] === true);
378
409
  }, [permissions]);
379
410
  const hasAllPermissions = useCallback2((permissionList) => {
380
- return permissionList.every((p) => !!permissions[p]);
411
+ if (permissions["*"]) {
412
+ return true;
413
+ }
414
+ return permissionList.every((p) => permissions[p] === true);
381
415
  }, [permissions]);
382
416
  const refetch = useCallback2(async () => {
383
417
  if (isFetchingRef.current) {
@@ -441,6 +475,7 @@ function useCan(userId, scope, permission, pageId, useCache = true) {
441
475
  return;
442
476
  }
443
477
  if (!scope.organisationId || scope.organisationId.trim() === "") {
478
+ console.log("[useCan] Invalid scope - organisationId is empty:", { scope, permission, pageId });
444
479
  setCan(false);
445
480
  setIsLoading(true);
446
481
  return;
@@ -448,9 +483,19 @@ function useCan(userId, scope, permission, pageId, useCache = true) {
448
483
  try {
449
484
  setIsLoading(true);
450
485
  setError(null);
486
+ console.log("[useCan] Checking permission:", {
487
+ userId,
488
+ organisationId: scope.organisationId,
489
+ eventId: scope.eventId,
490
+ appId: scope.appId,
491
+ permission,
492
+ pageId
493
+ });
451
494
  const result = useCache ? await isPermittedCached({ userId, scope, permission, pageId }) : await isPermitted({ userId, scope, permission, pageId });
495
+ console.log("[useCan] Permission check result:", { permission, result, pageId });
452
496
  setCan(result);
453
497
  } catch (err) {
498
+ console.error("[useCan] Permission check error:", { permission, error: err });
454
499
  setError(err instanceof Error ? err : new Error("Failed to check permission"));
455
500
  setCan(false);
456
501
  } finally {
@@ -684,4 +729,4 @@ export {
684
729
  useHasAllPermissions,
685
730
  useCachedPermissions
686
731
  };
687
- //# sourceMappingURL=chunk-KHJS6VIA.js.map
732
+ //# sourceMappingURL=chunk-PUKTJMRT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rbac/hooks/useRBAC.ts","../src/rbac/hooks/useResolvedScope.ts","../src/rbac/utils/eventContext.ts","../src/rbac/hooks/usePermissions.ts"],"sourcesContent":["/**\n * @file RBAC Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 0.3.0\n *\n * A React hook that provides access to the RBAC (Role-Based Access Control) system\n * through the hardened RBAC engine API. The hook defers all permission and role\n * resolution to the shared engine to ensure consistent security behaviour across\n * applications.\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';\nimport { useOrganisations } from '../../hooks/useOrganisations';\nimport { useEvents } from '../../hooks/useEvents';\nimport {\n getPermissionMap,\n getAccessLevel,\n isPermittedCached,\n resolveAppContext,\n getRoleContext,\n} from '../api';\nimport { getRBACLogger } from '../config';\nimport type {\n UserRBACContext,\n GlobalRole,\n OrganisationRole,\n EventAppRole,\n Operation,\n Permission,\n Scope,\n PermissionMap,\n UUID,\n} from '../types';\n\nfunction mapAccessLevelToEventRole(level: string | null): EventAppRole | null {\n switch (level) {\n case 'viewer':\n return 'viewer';\n case 'participant':\n return 'participant';\n case 'planner':\n return 'planner';\n case 'admin':\n case 'super':\n return 'event_admin';\n default:\n return null;\n }\n}\n\nexport function useRBAC(pageId?: string): UserRBACContext {\n const logger = getRBACLogger();\n const { user, session, appName } = useUnifiedAuth();\n const { selectedOrganisation } = useOrganisations();\n\n let selectedEvent: { event_id: string } | null = null;\n try {\n const eventsContext = useEvents();\n selectedEvent = eventsContext.selectedEvent;\n } catch (error) {\n logger.debug('[useRBAC] Event provider not available, continuing without event context', error);\n }\n\n const [globalRole, setGlobalRole] = useState<GlobalRole | null>(null);\n const [organisationRole, setOrganisationRole] = useState<OrganisationRole | null>(null);\n const [eventAppRole, setEventAppRole] = useState<EventAppRole | null>(null);\n const [permissionMap, setPermissionMap] = useState<PermissionMap>({} as PermissionMap);\n const [currentScope, setCurrentScope] = useState<Scope | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const resetState = useCallback(() => {\n setGlobalRole(null);\n setOrganisationRole(null);\n setEventAppRole(null);\n setPermissionMap({} as PermissionMap);\n setCurrentScope(null);\n }, []);\n\n const loadRBACContext = useCallback(async () => {\n if (!user || !session) {\n resetState();\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n let appId: UUID | undefined;\n if (appName) {\n const resolved = await resolveAppContext({ userId: user.id as UUID, appName });\n if (!resolved || !resolved.hasAccess) {\n throw new Error(`User does not have access to app \"${appName}\"`);\n }\n appId = resolved.appId;\n }\n\n const scope: Scope = {\n organisationId: selectedOrganisation?.id,\n eventId: selectedEvent?.event_id || undefined,\n appId,\n };\n setCurrentScope(scope);\n\n const [map, roleContext, accessLevel] = await Promise.all([\n getPermissionMap({ userId: user.id as UUID, scope }),\n getRoleContext({ userId: user.id as UUID, scope }),\n getAccessLevel({ userId: user.id as UUID, scope }),\n ]);\n\n setPermissionMap(map);\n setGlobalRole(roleContext.globalRole);\n setOrganisationRole(roleContext.organisationRole);\n setEventAppRole(roleContext.eventAppRole || mapAccessLevelToEventRole(accessLevel));\n } catch (err) {\n const handledError = err instanceof Error ? err : new Error('Failed to load RBAC context');\n logger.error('[useRBAC] Error loading RBAC context:', handledError);\n setError(handledError);\n resetState();\n } finally {\n setIsLoading(false);\n }\n }, [appName, logger, resetState, selectedEvent?.event_id, selectedOrganisation?.id, session, user]);\n\n const hasPermission = useCallback(\n async (operationOrPermission: Operation | string, targetPageId?: string): Promise<boolean> => {\n if (!user) {\n logger.warn('[useRBAC] Permission check attempted without authenticated user context');\n return false;\n }\n\n if (!currentScope || !currentScope.organisationId) {\n logger.error('[useRBAC] Permission check denied due to missing organisation context', {\n hasScope: !!currentScope,\n scope: currentScope,\n userId: user.id,\n permission: operationOrPermission,\n });\n return false;\n }\n\n if (globalRole === 'super_admin' || permissionMap['*']) {\n logger.info('[useRBAC] Super admin bypass granted', {\n userId: user.id,\n permission: operationOrPermission,\n });\n return true;\n }\n\n let permission: Permission;\n if (operationOrPermission.includes(':')) {\n permission = operationOrPermission as Permission;\n } else if (targetPageId || pageId) {\n permission = `${operationOrPermission}:${targetPageId || pageId}` as Permission;\n } else {\n permission = operationOrPermission as Permission;\n }\n\n const cachedValue = permissionMap[permission];\n if (cachedValue === true) {\n return true;\n }\n if (cachedValue === false) {\n return false;\n }\n\n return isPermittedCached({\n userId: user.id as UUID,\n scope: currentScope,\n permission,\n pageId: targetPageId ?? pageId,\n });\n },\n [currentScope, globalRole, logger, pageId, permissionMap, user],\n );\n\n const hasGlobalPermission = useCallback(\n (permission: string): boolean => {\n if (globalRole === 'super_admin' || permissionMap['*']) {\n return true;\n }\n\n if (permission === 'super_admin') {\n return globalRole === 'super_admin';\n }\n\n if (permission === 'org_admin') {\n return organisationRole === 'org_admin';\n }\n\n return permissionMap[permission as Permission] === true;\n },\n [globalRole, organisationRole, permissionMap],\n );\n\n const isSuperAdmin = useMemo(() => globalRole === 'super_admin' || permissionMap['*'] === true, [globalRole, permissionMap]);\n const isOrgAdmin = useMemo(() => organisationRole === 'org_admin' || isSuperAdmin, [organisationRole, isSuperAdmin]);\n const isEventAdmin = useMemo(() => eventAppRole === 'event_admin' || isSuperAdmin, [eventAppRole, isSuperAdmin]);\n const canManageOrganisation = useMemo(() => isSuperAdmin || organisationRole === 'org_admin', [isSuperAdmin, organisationRole]);\n const canManageEvent = useMemo(() => isSuperAdmin || eventAppRole === 'event_admin', [isSuperAdmin, eventAppRole]);\n\n useEffect(() => {\n loadRBACContext();\n }, [loadRBACContext]);\n\n return {\n user,\n globalRole,\n organisationRole,\n eventAppRole,\n hasPermission,\n hasGlobalPermission,\n isSuperAdmin,\n isOrgAdmin,\n isEventAdmin,\n canManageOrganisation,\n canManageEvent,\n isLoading,\n error,\n };\n}\n","/**\n * @file useResolvedScope Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * Shared hook for resolving RBAC scope from various contexts.\n * This hook is used by both DataTable and PagePermissionGuard to ensure\n * consistent scope resolution logic.\n */\n\nimport { useEffect, useState, useRef } from 'react';\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport type { Scope } from '../types';\nimport { createScopeFromEvent } from '../utils/eventContext';\nimport { getCurrentAppName } from '../../utils/appNameResolver';\n\nexport interface UseResolvedScopeOptions {\n /** Supabase client instance */\n supabase: SupabaseClient<Database> | null;\n /** Selected organisation ID */\n selectedOrganisationId: string | null;\n /** Selected event ID */\n selectedEventId: string | null;\n}\n\nexport interface UseResolvedScopeReturn {\n /** Resolved scope, or null if not yet resolved */\n resolvedScope: Scope | null;\n /** Whether the scope resolution is in progress */\n isLoading: boolean;\n /** Error if scope resolution failed */\n error: Error | null;\n}\n\n/**\n * Resolves RBAC scope from organisation and event context\n * \n * This hook handles the complex logic of determining the correct RBAC scope\n * based on available context (organisation, event, app). It ensures consistent\n * scope resolution across the application.\n * \n * @param options - Hook options\n * @returns Resolved scope and loading state\n * \n * @example\n * ```tsx\n * const { resolvedScope, isLoading } = useResolvedScope({\n * supabase,\n * selectedOrganisationId,\n * selectedEventId\n * });\n * \n * if (isLoading) return <Loading />;\n * if (!resolvedScope) return <Error />;\n * \n * const permission = useCan(userId, resolvedScope, permission);\n * ```\n */\nexport function useResolvedScope({\n supabase,\n selectedOrganisationId,\n selectedEventId\n}: UseResolvedScopeOptions): UseResolvedScopeReturn {\n const [resolvedScope, setResolvedScope] = useState<Scope | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n \n // Use a ref to track the stable scope and only update it when it actually changes\n const stableScopeRef = useRef<{ organisationId: string; appId: string; eventId: string | undefined }>({ \n organisationId: '', \n appId: '', \n eventId: undefined \n });\n \n // Only update the stable scope if the resolved scope has actually changed\n if (resolvedScope && resolvedScope.organisationId) {\n const newScope = {\n organisationId: resolvedScope.organisationId,\n appId: resolvedScope.appId,\n eventId: resolvedScope.eventId\n };\n \n // Only update if the scope has actually changed\n if (stableScopeRef.current.organisationId !== newScope.organisationId ||\n stableScopeRef.current.eventId !== newScope.eventId ||\n stableScopeRef.current.appId !== newScope.appId) {\n stableScopeRef.current = {\n organisationId: newScope.organisationId,\n appId: newScope.appId || '',\n eventId: newScope.eventId\n };\n }\n } else if (!resolvedScope) {\n // Reset to empty scope when no resolved scope\n stableScopeRef.current = { organisationId: '', appId: '', eventId: undefined };\n }\n \n const stableScope = stableScopeRef.current;\n \n useEffect(() => {\n let cancelled = false;\n \n const resolveScope = async () => {\n setIsLoading(true);\n setError(null);\n \n try {\n // Get app ID from package.json or environment\n let appId: string | undefined = undefined;\n \n // Try to resolve from database\n if (supabase) {\n const appName = getCurrentAppName();\n if (appName) {\n try {\n const { data: app, error } = await supabase\n .from('rbac_apps')\n .select('id, name, is_active')\n .eq('name', appName)\n .eq('is_active', true)\n .single() as { data: { id: string; name: string; is_active: boolean } | null; error: any };\n \n if (error) {\n // Check if app exists but is inactive\n const { data: inactiveApp } = await supabase\n .from('rbac_apps')\n .select('id, name, is_active')\n .eq('name', appName)\n .single() as { data: { id: string; name: string; is_active: boolean } | null };\n \n if (inactiveApp) {\n console.error(`[useResolvedScope] App \"${appName}\" exists but is inactive (is_active: ${inactiveApp.is_active})`);\n } else {\n console.error(`[useResolvedScope] App \"${appName}\" not found in rbac_apps table`);\n }\n } else if (app) {\n appId = app.id;\n }\n } catch (error) {\n console.error('[useResolvedScope] Unexpected error resolving app ID:', error);\n }\n }\n }\n\n // Debug logging\n console.log('[useResolvedScope] Attempting to resolve scope:', {\n selectedOrganisationId,\n selectedEventId,\n appId: appId || 'NOT RESOLVED YET',\n resolveStep: 'initial'\n });\n\n // If we have both organisation and event, use them directly\n if (selectedOrganisationId && selectedEventId) {\n console.log('[useResolvedScope] Resolving with both org and event');\n if (!cancelled) {\n setResolvedScope({\n organisationId: selectedOrganisationId,\n eventId: selectedEventId,\n appId: appId\n });\n setIsLoading(false);\n }\n return;\n }\n\n // If we only have organisation, use it\n if (selectedOrganisationId) {\n console.log('[useResolvedScope] Resolving with organisation only');\n if (!cancelled) {\n setResolvedScope({\n organisationId: selectedOrganisationId,\n eventId: selectedEventId || undefined,\n appId: appId\n });\n setIsLoading(false);\n }\n return;\n }\n\n // If we only have event, resolve organisation from event\n if (selectedEventId && supabase) {\n try {\n console.log('[useResolvedScope] Resolving from event:', { selectedEventId, appId });\n const eventScope = await createScopeFromEvent(supabase, selectedEventId, appId);\n if (!eventScope) {\n console.error('[useResolvedScope] Could not resolve organization from event context');\n if (!cancelled) {\n setResolvedScope(null);\n setError(new Error('Could not resolve organisation from event context'));\n setIsLoading(false);\n }\n return;\n }\n console.log('[useResolvedScope] Resolved from event:', eventScope);\n // Preserve the resolved app ID\n if (!cancelled) {\n setResolvedScope({\n ...eventScope,\n appId: appId || eventScope.appId\n });\n setIsLoading(false);\n }\n } catch (err) {\n console.error('[useResolvedScope] Error resolving scope from event:', err);\n if (!cancelled) {\n setResolvedScope(null);\n setError(err as Error);\n setIsLoading(false);\n }\n }\n return;\n }\n\n // No context available\n console.error('[useResolvedScope] No organisation or event context available');\n if (!cancelled) {\n setResolvedScope(null);\n setError(new Error('No organisation or event context available'));\n setIsLoading(false);\n }\n } catch (err) {\n if (!cancelled) {\n setError(err as Error);\n setIsLoading(false);\n }\n }\n };\n\n resolveScope();\n \n return () => {\n cancelled = true;\n };\n }, [selectedOrganisationId, selectedEventId, supabase]);\n \n return {\n resolvedScope: stableScope.organisationId ? stableScope as Scope : null,\n isLoading,\n error\n };\n}\n","/**\n * Event Context Utilities for RBAC\n * @package @jmruthers/pace-core\n * @module RBAC/EventContext\n * @since 1.0.0\n * \n * This module provides utilities for event-based RBAC operations where\n * the organization context is derived from the event context.\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { Database } from '../../types/database';\nimport { UUID, Scope } from '../types';\n\n/**\n * Get organization ID from event ID\n * \n * @param supabase - Supabase client\n * @param eventId - Event ID\n * @returns Promise resolving to organization ID or null\n */\nexport async function getOrganisationFromEvent(\n supabase: SupabaseClient<Database>,\n eventId: string\n): Promise<UUID | null> {\n const { data, error } = await supabase\n .from('event')\n .select('organisation_id')\n .eq('event_id', eventId)\n .single() as { data: { organisation_id: string } | null; error: any };\n\n if (error || !data) {\n return null;\n }\n\n return data.organisation_id;\n}\n\n/**\n * Create a complete scope from event context\n * \n * @param supabase - Supabase client\n * @param eventId - Event ID\n * @param appId - Optional app ID\n * @returns Promise resolving to complete scope\n */\nexport async function createScopeFromEvent(\n supabase: SupabaseClient<Database>,\n eventId: string,\n appId?: UUID\n): Promise<Scope | null> {\n const organisationId = await getOrganisationFromEvent(supabase, eventId);\n \n if (!organisationId) {\n return null;\n }\n\n return {\n organisationId,\n eventId,\n appId\n };\n}\n\n/**\n * Check if a scope is event-based (has eventId but no explicit organisationId)\n * \n * @param scope - Permission scope\n * @returns True if scope is event-based\n */\nexport function isEventBasedScope(scope: Scope): boolean {\n return !scope.organisationId && !!scope.eventId;\n}\n\n/**\n * Validate that an event-based scope has the required context\n * \n * @param scope - Permission scope\n * @returns True if scope is valid for event-based operations\n */\nexport function isValidEventBasedScope(scope: Scope): boolean {\n return isEventBasedScope(scope) && !!scope.eventId;\n}\n","/**\n * @file RBAC Permission Hooks\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * This module provides React hooks for RBAC functionality.\n */\n\nimport { useState, useEffect, useCallback, useMemo, useRef } from 'react';\nimport { \n UUID, \n Scope, \n Permission, \n PermissionMap\n} from '../types';\nimport { AccessLevel as AccessLevelType } from '../types';\nimport { \n getAccessLevel, \n getPermissionMap, \n isPermitted,\n isPermittedCached \n} from '../api';\n\n/**\n * Hook to get user's permissions in a scope\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @returns Permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error } = usePermissions(userId, scope);\n * \n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {permissions['read:users'] && <UserList />}\n * {permissions['create:users'] && <CreateUserButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePermissions(userId: UUID, scope: Scope) {\n const [permissions, setPermissions] = useState<PermissionMap>({} as PermissionMap);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const isFetchingRef = useRef(false);\n\n useEffect(() => {\n const fetchPermissions = async () => {\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n return;\n }\n\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n // Don't fetch permissions if scope is invalid (e.g., organisationId is empty)\n // Return empty permissions and loading true for invalid scopes to prevent premature access denied\n if (!scope.organisationId || scope.organisationId.trim() === '') {\n setPermissions({} as PermissionMap);\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n \n const permissionMap = await getPermissionMap({ userId, scope });\n setPermissions(permissionMap);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch permissions'));\n } finally {\n setIsLoading(false);\n isFetchingRef.current = false;\n }\n };\n\n fetchPermissions();\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n const hasPermission = useCallback((permission: Permission): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissions[permission] === true;\n }, [permissions]);\n\n const hasAnyPermission = useCallback((permissionList: Permission[]): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissionList.some(p => permissions[p] === true);\n }, [permissions]);\n\n const hasAllPermissions = useCallback((permissionList: Permission[]): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissionList.every(p => permissions[p] === true);\n }, [permissions]);\n\n const refetch = useCallback(async () => {\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n return;\n }\n\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n // Don't fetch permissions if scope is invalid (e.g., organisationId is empty)\n if (!scope.organisationId || scope.organisationId.trim() === '') {\n setPermissions({} as PermissionMap);\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n \n const permissionMap = await getPermissionMap({ userId, scope });\n setPermissions(permissionMap);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch permissions'));\n } finally {\n setIsLoading(false);\n isFetchingRef.current = false;\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n permissions,\n isLoading,\n error,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n refetch\n }), [permissions, isLoading, error, hasPermission, hasAnyPermission, hasAllPermissions, refetch]);\n}\n\n/**\n * Hook to check if user can perform an action\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permission - Permission to check\n * @param pageId - Optional page ID\n * @param useCache - Whether to use cached results\n * @returns Permission check state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { can, isLoading, error } = useCan(userId, scope, 'read:users');\n * \n * if (isLoading) return <div>Checking permission...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return can ? <UserList /> : <div>Access denied</div>;\n * }\n * ```\n */\nexport function useCan(\n userId: UUID, \n scope: Scope, \n permission: Permission, \n pageId?: UUID,\n useCache: boolean = true\n) {\n const [can, setCan] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Use refs to track the last values to prevent unnecessary re-runs\n const lastUserIdRef = useRef<UUID | null>(null);\n const lastScopeRef = useRef<string | null>(null);\n const lastPermissionRef = useRef<Permission | null>(null);\n const lastPageIdRef = useRef<UUID | undefined | null>(null);\n const lastUseCacheRef = useRef<boolean | null>(null);\n\n useEffect(() => {\n // Create a scope key to track changes\n const scopeKey = `${scope.organisationId}-${scope.eventId}-${scope.appId}`;\n \n // Only run if something has actually changed\n if (\n lastUserIdRef.current !== userId ||\n lastScopeRef.current !== scopeKey ||\n lastPermissionRef.current !== permission ||\n lastPageIdRef.current !== pageId ||\n lastUseCacheRef.current !== useCache\n ) {\n lastUserIdRef.current = userId;\n lastScopeRef.current = scopeKey;\n lastPermissionRef.current = permission;\n lastPageIdRef.current = pageId;\n lastUseCacheRef.current = useCache;\n \n // Inline the permission check logic to avoid useCallback dependency issues\n const checkPermission = async () => {\n if (!userId) {\n setCan(false);\n setIsLoading(false);\n return;\n }\n\n // Don't check permissions if scope is invalid (e.g., organisationId is empty)\n // Return can: false and loading: true for invalid scopes to prevent premature access denied\n if (!scope.organisationId || scope.organisationId.trim() === '') {\n console.log('[useCan] Invalid scope - organisationId is empty:', { scope, permission, pageId });\n setCan(false);\n setIsLoading(true);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n console.log('[useCan] Checking permission:', { \n userId, \n organisationId: scope.organisationId, \n eventId: scope.eventId, \n appId: scope.appId,\n permission, \n pageId \n });\n \n const result = useCache \n ? await isPermittedCached({ userId, scope, permission, pageId })\n : await isPermitted({ userId, scope, permission, pageId });\n \n console.log('[useCan] Permission check result:', { permission, result, pageId });\n \n setCan(result);\n } catch (err) {\n console.error('[useCan] Permission check error:', { permission, error: err });\n setError(err instanceof Error ? err : new Error('Failed to check permission'));\n setCan(false);\n } finally {\n setIsLoading(false);\n }\n };\n \n checkPermission();\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permission, pageId, useCache]);\n\n const refetch = useCallback(async () => {\n if (!userId) {\n setCan(false);\n setIsLoading(false);\n return;\n }\n\n // Don't check permissions if scope is invalid (e.g., organisationId is empty)\n if (!scope.organisationId || scope.organisationId.trim() === '') {\n setCan(false);\n setIsLoading(true);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const result = useCache \n ? await isPermittedCached({ userId, scope, permission, pageId })\n : await isPermitted({ userId, scope, permission, pageId });\n \n setCan(result);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permission'));\n setCan(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permission, pageId, useCache]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n can,\n isLoading,\n error,\n refetch\n }), [can, isLoading, error, refetch]);\n}\n\n/**\n * Hook to get user's access level in a scope\n * \n * @param userId - User ID\n * @param scope - Scope for access level checking\n * @returns Access level state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { accessLevel, isLoading, error } = useAccessLevel(userId, scope);\n * \n * if (isLoading) return <div>Loading access level...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * Access Level: {accessLevel}\n * {accessLevel >= AccessLevel.ADMIN && <AdminPanel />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useAccessLevel(userId: UUID, scope: Scope): {\n accessLevel: AccessLevelType;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [accessLevel, setAccessLevel] = useState<AccessLevelType>('viewer');\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchAccessLevel = useCallback(async () => {\n if (!userId) {\n setAccessLevel('viewer');\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const level = await getAccessLevel({ userId, scope });\n setAccessLevel(level);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch access level'));\n setAccessLevel('viewer');\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n useEffect(() => {\n fetchAccessLevel();\n }, [fetchAccessLevel]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n accessLevel,\n isLoading,\n error,\n refetch: fetchAccessLevel\n }), [accessLevel, isLoading, error, fetchAccessLevel]);\n}\n\n/**\n * Hook to check multiple permissions at once\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Multiple permission check results\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { results, isLoading, error } = useMultiplePermissions(\n * userId, \n * scope, \n * ['read:users', 'create:users', 'update:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {results['read:users'] && <UserList />}\n * {results['create:users'] && <CreateUserButton />}\n * {results['update:users'] && <EditUserButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMultiplePermissions(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n results: Record<Permission, boolean>;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [results, setResults] = useState<Record<Permission, boolean>>({} as Record<Permission, boolean>);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkPermissions = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setResults({} as Record<Permission, boolean>);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const permissionResults: Record<Permission, boolean> = {} as Record<Permission, boolean>;\n \n // Check each permission\n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n permissionResults[permission] = result;\n }\n \n setResults(permissionResults);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setResults({} as Record<Permission, boolean>);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkPermissions();\n }, [checkPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n results,\n isLoading,\n error,\n refetch: checkPermissions\n }), [results, isLoading, error, checkPermissions]);\n}\n\n/**\n * Hook to check if user has any of the specified permissions\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Whether user has any of the permissions\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { hasAny, isLoading, error } = useHasAnyPermission(\n * userId, \n * scope, \n * ['read:users', 'create:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return hasAny ? <UserManagementPanel /> : <div>No user permissions</div>;\n * }\n * ```\n */\nexport function useHasAnyPermission(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n hasAny: boolean;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [hasAny, setHasAny] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkAnyPermission = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setHasAny(false);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n let hasAnyPermission = false;\n \n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n \n if (result) {\n hasAnyPermission = true;\n break;\n }\n }\n \n setHasAny(hasAnyPermission);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setHasAny(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkAnyPermission();\n }, [checkAnyPermission]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n hasAny,\n isLoading,\n error,\n refetch: checkAnyPermission\n }), [hasAny, isLoading, error, checkAnyPermission]);\n}\n\n/**\n * Hook to check if user has all of the specified permissions\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Whether user has all of the permissions\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { hasAll, isLoading, error } = useHasAllPermissions(\n * userId, \n * scope, \n * ['read:users', 'create:users', 'update:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return hasAll ? <FullUserManagementPanel /> : <div>Insufficient permissions</div>;\n * }\n * ```\n */\nexport function useHasAllPermissions(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n hasAll: boolean;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [hasAll, setHasAll] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkAllPermissions = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setHasAll(false);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n let hasAllPermissions = true;\n \n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n \n if (!result) {\n hasAllPermissions = false;\n break;\n }\n }\n \n setHasAll(hasAllPermissions);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setHasAll(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkAllPermissions();\n }, [checkAllPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n hasAll,\n isLoading,\n error,\n refetch: checkAllPermissions\n }), [hasAll, isLoading, error, checkAllPermissions]);\n}\n\n/**\n * Hook to get cached permissions with TTL management\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @returns Cached permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error, invalidateCache } = useCachedPermissions(userId, scope);\n * \n * if (isLoading) return <div>Loading cached permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {permissions['read:users'] && <UserList />}\n * <button onClick={invalidateCache}>Refresh Permissions</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useCachedPermissions(userId: UUID, scope: Scope): {\n permissions: PermissionMap;\n isLoading: boolean;\n error: Error | null;\n invalidateCache: () => void;\n refetch: () => Promise<void>;\n} {\n const [permissions, setPermissions] = useState<PermissionMap>({} as PermissionMap);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchCachedPermissions = useCallback(async () => {\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const permissionMap = await getPermissionMap({ userId, scope });\n setPermissions(permissionMap);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch cached permissions'));\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n const invalidateCache = useCallback(() => {\n // This would typically invalidate the cache in the actual implementation\n // For now, we'll just refetch\n fetchCachedPermissions();\n }, [fetchCachedPermissions]);\n\n useEffect(() => {\n fetchCachedPermissions();\n }, [fetchCachedPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n permissions,\n isLoading,\n error,\n invalidateCache,\n refetch: fetchCachedPermissions\n }), [permissions, isLoading, error, invalidateCache, fetchCachedPermissions]);\n}\n"],"mappings":"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","names":["error","useEffect","useState","useState","useEffect","error","useState","useEffect","useCallback","useMemo","useRef","useState","useRef","useEffect","useCallback","useMemo"]}
@@ -30,19 +30,27 @@ var RBACAuditManager = class {
30
30
  if (!this.enabled) {
31
31
  return;
32
32
  }
33
- if (!event.userId || !event.organisationId) {
34
- console.warn("[RBAC Audit] Skipping audit event - missing required fields:", {
35
- userId: event.userId,
36
- organisationId: event.organisationId,
37
- eventType: event.type
33
+ if (!event.userId) {
34
+ console.error("[RBAC Audit] CRITICAL: Cannot log audit event without userId:", {
35
+ eventType: event.type,
36
+ organisationId: event.organisationId
38
37
  });
39
38
  return;
40
39
  }
40
+ if (!event.organisationId) {
41
+ console.warn("[RBAC Audit] Audit event without organisation context:", {
42
+ userId: event.userId,
43
+ eventType: event.type,
44
+ note: "This should be investigated for security compliance"
45
+ });
46
+ }
41
47
  try {
42
48
  const auditEvent = {
43
49
  event_type: event.type,
44
50
  user_id: event.userId,
45
- organisation_id: event.organisationId,
51
+ // CRITICAL: Store organisationId even if null for auditing
52
+ // Use a fallback UUID if organisation context is missing (for database constraint)
53
+ organisation_id: event.organisationId || "00000000-0000-0000-0000-000000000000",
46
54
  event_id: "eventId" in event ? event.eventId : void 0,
47
55
  app_id: "appId" in event ? event.appId : void 0,
48
56
  page_id: "pageId" in event ? event.pageId : void 0,
@@ -55,7 +63,9 @@ var RBACAuditManager = class {
55
63
  metadata: {
56
64
  ...event.metadata,
57
65
  cache_hit: "cache_hit" in event ? event.cache_hit : void 0,
58
- cache_source: "cache_source" in event ? event.cache_source : void 0
66
+ cache_source: "cache_source" in event ? event.cache_source : void 0,
67
+ // Store a flag indicating this event had no organisation context
68
+ no_organisation_context: !event.organisationId
59
69
  }
60
70
  };
61
71
  const { error } = await this.supabase.from("rbac_audit_events").insert([auditEvent]);
@@ -179,4 +189,4 @@ export {
179
189
  getGlobalAuditManager,
180
190
  emitAuditEvent
181
191
  };
182
- //# sourceMappingURL=chunk-B2WTCLCV.js.map
192
+ //# sourceMappingURL=chunk-Q7APDV6H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rbac/audit.ts"],"sourcesContent":["/**\n * RBAC Audit Events System\n * @package @jmruthers/pace-core\n * @module RBAC/Audit\n * @since 1.0.0\n * \n * This module provides structured audit event emission for all RBAC operations.\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { Database } from '../types/database';\nimport { \n UUID, \n AuditEventSource, \n RBACAuditEvent \n} from './types';\n\n/**\n * Audit event payload for permission checks\n */\nexport interface PermissionCheckAuditEvent {\n type: 'permission_check';\n userId: UUID;\n organisationId: UUID;\n eventId?: string;\n appId?: UUID;\n pageId?: UUID;\n permission: string;\n decision: boolean;\n source: AuditEventSource;\n bypass?: boolean;\n duration_ms: number;\n cache_hit?: boolean;\n cache_source?: 'memory' | 'database' | 'rpc';\n metadata?: Record<string, any>;\n}\n\n/**\n * Audit event payload for permission denied\n */\nexport interface PermissionDeniedAuditEvent {\n type: 'permission_denied';\n userId: UUID;\n organisationId: UUID;\n eventId?: string;\n appId?: UUID;\n pageId?: UUID;\n permission: string;\n source: AuditEventSource;\n metadata?: Record<string, any>;\n}\n\n/**\n * Audit event payload for role granted\n */\nexport interface RoleGrantedAuditEvent {\n type: 'role_granted';\n userId: UUID;\n organisationId: UUID;\n eventId?: string;\n appId?: UUID;\n role: string;\n grantedBy: UUID;\n metadata?: Record<string, any>;\n}\n\n/**\n * Audit event payload for role revoked\n */\nexport interface RoleRevokedAuditEvent {\n type: 'role_denied';\n userId: UUID;\n organisationId: UUID;\n eventId?: string;\n appId?: UUID;\n role: string;\n revokedBy: UUID;\n metadata?: Record<string, any>;\n}\n\n/**\n * Audit event payload for RLS denied\n */\nexport interface RLSDeniedAuditEvent {\n type: 'rls_denied';\n userId: UUID;\n organisationId: UUID;\n table: string;\n operation: string;\n metadata?: Record<string, any>;\n}\n\n/**\n * Union type for all audit events\n */\nexport type AuditEventPayload = \n | PermissionCheckAuditEvent\n | PermissionDeniedAuditEvent\n | RoleGrantedAuditEvent\n | RoleRevokedAuditEvent\n | RLSDeniedAuditEvent;\n\n/**\n * RBAC Audit Manager\n * \n * Handles emission of structured audit events for all RBAC operations.\n */\nexport class RBACAuditManager {\n private supabase: SupabaseClient<Database>;\n private enabled: boolean = true;\n\n constructor(supabase: SupabaseClient<Database>) {\n this.supabase = supabase;\n }\n\n /**\n * Enable or disable audit logging\n * \n * @param enabled - Whether to enable audit logging\n */\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n }\n\n /**\n * Check if audit logging is enabled\n * \n * @returns True if audit logging is enabled\n */\n isEnabled(): boolean {\n return this.enabled;\n }\n\n /**\n * Emit an audit event\n * \n * @param event - Audit event payload\n * @returns Promise that resolves when event is logged\n */\n async emitEvent(event: AuditEventPayload): Promise<void> {\n if (!this.enabled) {\n return;\n }\n\n // Validate required fields before attempting to insert\n // MANDATORY: All audit events must have userId\n if (!event.userId) {\n console.error('[RBAC Audit] CRITICAL: Cannot log audit event without userId:', {\n eventType: event.type,\n organisationId: event.organisationId\n });\n return;\n }\n\n // WARNING: Some audit events may not have organisationId (e.g., global admin operations)\n // Log these for security monitoring even if organisationId is missing\n if (!event.organisationId) {\n console.warn('[RBAC Audit] Audit event without organisation context:', {\n userId: event.userId,\n eventType: event.type,\n note: 'This should be investigated for security compliance'\n });\n }\n\n try {\n // For events without organisationId, store in a special way\n const auditEvent: Omit<RBACAuditEvent, 'id' | 'created_at'> = {\n event_type: event.type,\n user_id: event.userId,\n // CRITICAL: Store organisationId even if null for auditing\n // Use a fallback UUID if organisation context is missing (for database constraint)\n organisation_id: event.organisationId || '00000000-0000-0000-0000-000000000000' as UUID,\n event_id: 'eventId' in event ? event.eventId : undefined,\n app_id: 'appId' in event ? event.appId : undefined,\n page_id: 'pageId' in event ? event.pageId : undefined,\n permission: 'permission' in event ? event.permission : undefined,\n decision: 'decision' in event ? event.decision : undefined,\n source: 'source' in event ? event.source : 'api', // Default to 'api' if not provided\n bypass: 'bypass' in event ? event.bypass : undefined,\n duration_ms: 'duration_ms' in event ? event.duration_ms : undefined,\n metadata: {\n ...event.metadata,\n cache_hit: 'cache_hit' in event ? event.cache_hit : undefined,\n cache_source: 'cache_source' in event ? event.cache_source : undefined,\n // Store a flag indicating this event had no organisation context\n no_organisation_context: !event.organisationId,\n },\n };\n\n const { error } = await (this.supabase as any)\n .from('rbac_audit_events')\n .insert([auditEvent]);\n\n if (error) {\n // Log the error for debugging but don't throw\n console.warn('[RBAC Audit] Failed to insert audit event:', {\n error: error.message,\n code: error.code,\n details: error.details,\n hint: error.hint,\n event: auditEvent\n });\n }\n } catch (error) {\n // Log unexpected errors but don't throw\n console.error('[RBAC Audit] Unexpected error during audit logging:', error);\n }\n }\n\n /**\n * Emit a permission check audit event\n * \n * @param event - Permission check event data\n */\n async emitPermissionCheck(event: Omit<PermissionCheckAuditEvent, 'type'>): Promise<void> {\n await this.emitEvent({\n type: 'permission_check',\n ...event,\n });\n }\n\n /**\n * Emit a permission denied audit event\n * \n * @param event - Permission denied event data\n */\n async emitPermissionDenied(event: Omit<PermissionDeniedAuditEvent, 'type'>): Promise<void> {\n await this.emitEvent({\n type: 'permission_denied',\n ...event,\n });\n }\n\n /**\n * Emit a role granted audit event\n * \n * @param event - Role granted event data\n */\n async emitRoleGranted(event: Omit<RoleGrantedAuditEvent, 'type'>): Promise<void> {\n await this.emitEvent({\n type: 'role_granted',\n ...event,\n });\n }\n\n /**\n * Emit a role revoked audit event\n * \n * @param event - Role revoked event data\n */\n async emitRoleRevoked(event: Omit<RoleRevokedAuditEvent, 'type'>): Promise<void> {\n await this.emitEvent({\n type: 'role_denied',\n ...event,\n });\n }\n\n /**\n * Emit an RLS denied audit event\n * \n * @param event - RLS denied event data\n */\n async emitRLSDenied(event: Omit<RLSDeniedAuditEvent, 'type'>): Promise<void> {\n await this.emitEvent({\n type: 'rls_denied',\n ...event,\n });\n }\n\n /**\n * Get audit events for a user\n * \n * @param userId - User ID\n * @param limit - Maximum number of events to return\n * @returns Promise resolving to audit events\n */\n async getUserAuditEvents(userId: UUID, limit: number = 100): Promise<RBACAuditEvent[]> {\n const { data, error } = await this.supabase\n .from('rbac_audit_events')\n .select('*')\n .eq('user_id', userId)\n .order('created_at', { ascending: false })\n .limit(limit);\n\n if (error) {\n throw new Error(`Failed to get audit events: ${error.message}`);\n }\n\n return data || [];\n }\n\n /**\n * Get audit events for an organisation\n * \n * @param organisationId - Organisation ID\n * @param limit - Maximum number of events to return\n * @returns Promise resolving to audit events\n */\n async getOrganisationAuditEvents(organisationId: UUID, limit: number = 100): Promise<RBACAuditEvent[]> {\n const { data, error } = await this.supabase\n .from('rbac_audit_events')\n .select('*')\n .eq('organisation_id', organisationId)\n .order('created_at', { ascending: false })\n .limit(limit);\n\n if (error) {\n throw new Error(`Failed to get audit events: ${error.message}`);\n }\n\n return data || [];\n }\n}\n\n/**\n * Create an audit manager instance\n * \n * @param supabase - Supabase client\n * @returns RBACAuditManager instance\n */\nexport function createAuditManager(supabase: SupabaseClient<Database>): RBACAuditManager {\n return new RBACAuditManager(supabase);\n}\n\n/**\n * Global audit manager instance\n * \n * This is set by the RBAC engine when it initializes.\n */\nlet globalAuditManager: RBACAuditManager | null = null;\n\n/**\n * Set the global audit manager\n * \n * @param manager - Audit manager instance\n */\nexport function setGlobalAuditManager(manager: RBACAuditManager): void {\n globalAuditManager = manager;\n}\n\n/**\n * Get the global audit manager\n * \n * @returns Global audit manager or null if not set\n */\nexport function getGlobalAuditManager(): RBACAuditManager | null {\n return globalAuditManager;\n}\n\n/**\n * Emit an audit event using the global audit manager\n * \n * @param event - Audit event payload\n */\nexport async function emitAuditEvent(event: AuditEventPayload): Promise<void> {\n if (globalAuditManager) {\n await globalAuditManager.emitEvent(event);\n }\n}\n"],"mappings":"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","names":[]}