@jmruthers/pace-core 0.5.193 → 0.6.2

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 (577) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +7 -1
  3. package/cursor-rules/00-pace-core-compliance.mdc +299 -0
  4. package/cursor-rules/01-standards-compliance.mdc +244 -0
  5. package/cursor-rules/02-project-structure.mdc +200 -0
  6. package/cursor-rules/03-solid-principles.mdc +222 -0
  7. package/cursor-rules/04-testing-standards.mdc +268 -0
  8. package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
  9. package/cursor-rules/06-code-quality.mdc +309 -0
  10. package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
  11. package/cursor-rules/08-markup-quality.mdc +452 -0
  12. package/cursor-rules/CHANGELOG.md +119 -0
  13. package/cursor-rules/README.md +192 -0
  14. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  15. package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
  16. package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
  17. package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
  18. package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  19. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  20. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  21. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  22. package/dist/chunk-24UVZUZG.js.map +1 -0
  23. package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
  24. package/dist/chunk-2UOI2FG5.js.map +1 -0
  25. package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
  26. package/dist/chunk-3XC4CPTD.js.map +1 -0
  27. package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
  28. package/dist/chunk-6J4GEEJR.js.map +1 -0
  29. package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
  30. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  31. package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
  32. package/dist/chunk-EHMR7VYL.js.map +1 -0
  33. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  34. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  35. package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
  36. package/dist/chunk-FFQEQTNW.js.map +1 -0
  37. package/dist/chunk-FMUCXFII.js +76 -0
  38. package/dist/chunk-FMUCXFII.js.map +1 -0
  39. package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
  40. package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
  41. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  42. package/dist/chunk-L4OXEN46.js.map +1 -0
  43. package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
  44. package/dist/chunk-M43Y4SSO.js.map +1 -0
  45. package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
  46. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  47. package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
  48. package/dist/chunk-NECFR5MM.js.map +1 -0
  49. package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
  50. package/dist/chunk-SFZUDBL5.js.map +1 -0
  51. package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
  52. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  53. package/dist/components.d.ts +6 -6
  54. package/dist/components.js +15 -12
  55. package/dist/components.js.map +1 -1
  56. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  57. package/dist/hooks.d.ts +59 -126
  58. package/dist/hooks.js +19 -28
  59. package/dist/hooks.js.map +1 -1
  60. package/dist/index.d.ts +63 -16
  61. package/dist/index.js +23 -24
  62. package/dist/index.js.map +1 -1
  63. package/dist/providers.d.ts +21 -3
  64. package/dist/providers.js +2 -2
  65. package/dist/rbac/index.d.ts +146 -115
  66. package/dist/rbac/index.js +8 -11
  67. package/dist/theming/runtime.d.ts +1 -13
  68. package/dist/theming/runtime.js +1 -1
  69. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  70. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  71. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  72. package/dist/types.d.ts +2 -2
  73. package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
  74. package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
  75. package/dist/utils.d.ts +4 -5
  76. package/dist/utils.js +15 -15
  77. package/dist/utils.js.map +1 -1
  78. package/docs/api/README.md +7 -1
  79. package/docs/api/classes/ColumnFactory.md +8 -8
  80. package/docs/api/classes/InvalidScopeError.md +4 -4
  81. package/docs/api/classes/Logger.md +1 -1
  82. package/docs/api/classes/MissingUserContextError.md +4 -4
  83. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  84. package/docs/api/classes/PermissionDeniedError.md +4 -4
  85. package/docs/api/classes/RBACAuditManager.md +1 -1
  86. package/docs/api/classes/RBACCache.md +1 -1
  87. package/docs/api/classes/RBACEngine.md +1 -1
  88. package/docs/api/classes/RBACError.md +4 -4
  89. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  90. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  91. package/docs/api/classes/StorageUtils.md +1 -1
  92. package/docs/api/enums/FileCategory.md +1 -1
  93. package/docs/api/enums/LogLevel.md +1 -1
  94. package/docs/api/enums/RBACErrorCode.md +1 -1
  95. package/docs/api/enums/RPCFunction.md +1 -1
  96. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  97. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  98. package/docs/api/interfaces/AggregateConfig.md +4 -4
  99. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  100. package/docs/api/interfaces/AvatarProps.md +1 -1
  101. package/docs/api/interfaces/BadgeProps.md +9 -2
  102. package/docs/api/interfaces/ButtonProps.md +7 -4
  103. package/docs/api/interfaces/CalendarProps.md +8 -5
  104. package/docs/api/interfaces/CardProps.md +8 -5
  105. package/docs/api/interfaces/ColorPalette.md +1 -1
  106. package/docs/api/interfaces/ColorShade.md +1 -1
  107. package/docs/api/interfaces/ComplianceResult.md +1 -1
  108. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  109. package/docs/api/interfaces/DataRecord.md +1 -1
  110. package/docs/api/interfaces/DataTableAction.md +24 -21
  111. package/docs/api/interfaces/DataTableColumn.md +31 -31
  112. package/docs/api/interfaces/DataTableProps.md +1 -1
  113. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  114. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  115. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  116. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  117. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  118. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  119. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  120. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  121. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  122. package/docs/api/interfaces/ExportColumn.md +1 -1
  123. package/docs/api/interfaces/ExportOptions.md +8 -8
  124. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  125. package/docs/api/interfaces/FileMetadata.md +1 -1
  126. package/docs/api/interfaces/FileReference.md +1 -1
  127. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  128. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  129. package/docs/api/interfaces/FileUploadProps.md +26 -23
  130. package/docs/api/interfaces/FooterProps.md +10 -8
  131. package/docs/api/interfaces/FormFieldProps.md +10 -10
  132. package/docs/api/interfaces/FormProps.md +1 -1
  133. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  134. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  135. package/docs/api/interfaces/InputProps.md +7 -4
  136. package/docs/api/interfaces/LabelProps.md +1 -1
  137. package/docs/api/interfaces/LoggerConfig.md +1 -1
  138. package/docs/api/interfaces/LoginFormProps.md +14 -11
  139. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  140. package/docs/api/interfaces/NavigationContextType.md +1 -1
  141. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  142. package/docs/api/interfaces/NavigationItem.md +11 -11
  143. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  144. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  145. package/docs/api/interfaces/Organisation.md +1 -1
  146. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  147. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  148. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  149. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  150. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  151. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  152. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  153. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  154. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  155. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  156. package/docs/api/interfaces/PaletteData.md +1 -1
  157. package/docs/api/interfaces/ParsedAddress.md +1 -1
  158. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  159. package/docs/api/interfaces/ProgressProps.md +1 -1
  160. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  161. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  162. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  163. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  164. package/docs/api/interfaces/QuickFix.md +1 -1
  165. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  166. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  167. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  168. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  169. package/docs/api/interfaces/RBACConfig.md +1 -1
  170. package/docs/api/interfaces/RBACContext.md +1 -1
  171. package/docs/api/interfaces/RBACLogger.md +1 -1
  172. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  173. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  174. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  175. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  176. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  177. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  178. package/docs/api/interfaces/RBACResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  181. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  182. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  183. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  184. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  185. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  186. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  187. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  188. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  189. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  190. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  191. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  192. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  193. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  194. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  195. package/docs/api/interfaces/RouteConfig.md +1 -1
  196. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  197. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  198. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  199. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  200. package/docs/api/interfaces/SetupIssue.md +1 -1
  201. package/docs/api/interfaces/StorageConfig.md +1 -1
  202. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  203. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  204. package/docs/api/interfaces/StorageListOptions.md +1 -1
  205. package/docs/api/interfaces/StorageListResult.md +1 -1
  206. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  207. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  208. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  209. package/docs/api/interfaces/StyleImport.md +1 -1
  210. package/docs/api/interfaces/SwitchProps.md +1 -1
  211. package/docs/api/interfaces/TabsContentProps.md +1 -1
  212. package/docs/api/interfaces/TabsListProps.md +1 -1
  213. package/docs/api/interfaces/TabsProps.md +1 -1
  214. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  215. package/docs/api/interfaces/TextareaProps.md +1 -1
  216. package/docs/api/interfaces/ToastActionElement.md +4 -1
  217. package/docs/api/interfaces/ToastProps.md +1 -1
  218. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  219. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  220. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  221. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  222. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  223. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  224. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  225. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  226. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  227. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  228. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  229. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  230. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  231. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  232. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  233. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  234. package/docs/api/interfaces/UserEventAccess.md +14 -11
  235. package/docs/api/interfaces/UserMenuProps.md +8 -6
  236. package/docs/api/interfaces/UserProfile.md +1 -1
  237. package/docs/api/modules.md +575 -634
  238. package/docs/architecture/database-schema-requirements.md +161 -0
  239. package/docs/core-concepts/rbac-system.md +3 -3
  240. package/docs/documentation-index.md +2 -4
  241. package/docs/getting-started/cursor-rules.md +263 -0
  242. package/docs/getting-started/installation-guide.md +6 -1
  243. package/docs/getting-started/quick-start.md +6 -1
  244. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  245. package/docs/migration/MIGRATION_GUIDE.md +6 -28
  246. package/docs/migration/README.md +52 -6
  247. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  248. package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
  249. package/docs/migration/database-changes-december-2025.md +3 -3
  250. package/docs/rbac/event-based-apps.md +1 -1
  251. package/docs/rbac/getting-started.md +1 -1
  252. package/docs/rbac/quick-start.md +1 -1
  253. package/docs/standards/README.md +40 -0
  254. package/docs/troubleshooting/migration.md +4 -4
  255. package/examples/PublicPages/PublicEventPage.tsx +1 -1
  256. package/package.json +12 -6
  257. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  258. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  259. package/scripts/audit/core/checks/bundle.cjs +142 -0
  260. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +737 -691
  261. package/scripts/audit/core/checks/config.cjs +54 -0
  262. package/scripts/audit/core/checks/coverage.cjs +84 -0
  263. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  264. package/scripts/audit/core/checks/documentation.cjs +203 -0
  265. package/scripts/audit/core/checks/environment.cjs +128 -0
  266. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  267. package/scripts/audit/core/checks/forms.cjs +172 -0
  268. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  269. package/scripts/audit/core/checks/hooks.cjs +334 -0
  270. package/scripts/audit/core/checks/imports.cjs +244 -0
  271. package/scripts/audit/core/checks/performance.cjs +325 -0
  272. package/scripts/audit/core/checks/routes.cjs +117 -0
  273. package/scripts/audit/core/checks/state.cjs +130 -0
  274. package/scripts/audit/core/checks/structure.cjs +65 -0
  275. package/scripts/audit/core/checks/style.cjs +584 -0
  276. package/scripts/audit/core/checks/testing.cjs +122 -0
  277. package/scripts/audit/core/checks/typescript.cjs +61 -0
  278. package/scripts/audit/core/scanner.cjs +199 -0
  279. package/scripts/audit/core/utils.cjs +137 -0
  280. package/scripts/audit/index.cjs +223 -0
  281. package/scripts/audit/reporters/console.cjs +151 -0
  282. package/scripts/audit/reporters/json.cjs +54 -0
  283. package/scripts/audit/reporters/markdown.cjs +124 -0
  284. package/scripts/audit-consuming-app.cjs +86 -0
  285. package/scripts/build-docs/build-decision.js +240 -0
  286. package/scripts/build-docs/cache-utils.js +105 -0
  287. package/scripts/build-docs/content-normalization.js +150 -0
  288. package/scripts/build-docs/file-utils.js +105 -0
  289. package/scripts/build-docs/git-utils.js +86 -0
  290. package/scripts/build-docs/hash-utils.js +116 -0
  291. package/scripts/build-docs/typedoc-runner.js +220 -0
  292. package/scripts/build-docs-incremental.js +77 -913
  293. package/scripts/install-cursor-rules.cjs +236 -0
  294. package/scripts/utils/command-runner.js +16 -11
  295. package/scripts/validate-formats.js +61 -56
  296. package/scripts/validate-master.js +74 -69
  297. package/scripts/validate-pre-publish.js +70 -65
  298. package/src/__tests__/helpers/test-providers.tsx +1 -1
  299. package/src/__tests__/helpers/test-utils.tsx +1 -1
  300. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  301. package/src/components/Alert/Alert.test.tsx +12 -18
  302. package/src/components/Alert/Alert.tsx +5 -7
  303. package/src/components/Avatar/Avatar.test.tsx +4 -4
  304. package/src/components/Badge/Badge.tsx +16 -4
  305. package/src/components/Button/Button.tsx +27 -4
  306. package/src/components/Calendar/Calendar.tsx +9 -3
  307. package/src/components/Card/Card.tsx +4 -0
  308. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  309. package/src/components/Checkbox/Checkbox.tsx +2 -2
  310. package/src/components/DataTable/DataTable.test.tsx +57 -93
  311. package/src/components/DataTable/DataTable.tsx +40 -6
  312. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  313. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
  314. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
  315. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  316. package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
  317. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  318. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  319. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  320. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  321. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  322. package/src/components/DataTable/components/DataTableCore.tsx +200 -561
  323. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  324. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  325. package/src/components/DataTable/components/DataTableModals.tsx +9 -1
  326. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  327. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  328. package/src/components/DataTable/components/EditFields.tsx +307 -0
  329. package/src/components/DataTable/components/EditableRow.tsx +9 -1
  330. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  331. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  332. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  333. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  334. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  335. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  336. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  337. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  338. package/src/components/DataTable/components/UnifiedTableBody.tsx +62 -852
  339. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  340. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  341. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
  342. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
  343. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
  344. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
  345. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
  346. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
  347. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
  348. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  349. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  350. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  351. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  352. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  353. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  354. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  355. package/src/components/DataTable/hooks/useColumnReordering.ts +14 -2
  356. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  357. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  358. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  359. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  360. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  361. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
  362. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  363. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  364. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  365. package/src/components/DataTable/styles.ts +6 -6
  366. package/src/components/DataTable/types.ts +6 -10
  367. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  368. package/src/components/DataTable/utils/debugTools.ts +18 -113
  369. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  370. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  371. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  372. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  373. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  374. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
  375. package/src/components/Dialog/Dialog.tsx +8 -7
  376. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  377. package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
  378. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  379. package/src/components/ErrorBoundary/index.ts +27 -2
  380. package/src/components/EventSelector/EventSelector.tsx +4 -1
  381. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
  382. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  383. package/src/components/FileUpload/FileUpload.tsx +22 -2
  384. package/src/components/Footer/Footer.test.tsx +16 -16
  385. package/src/components/Footer/Footer.tsx +15 -12
  386. package/src/components/Form/Form.test.tsx +36 -15
  387. package/src/components/Form/Form.tsx +31 -26
  388. package/src/components/Header/Header.tsx +22 -11
  389. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
  390. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
  391. package/src/components/Input/Input.test.tsx +2 -2
  392. package/src/components/Input/Input.tsx +36 -34
  393. package/src/components/Label/Label.tsx +1 -1
  394. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  395. package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
  396. package/src/components/LoginForm/LoginForm.test.tsx +42 -42
  397. package/src/components/LoginForm/LoginForm.tsx +12 -8
  398. package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
  399. package/src/components/NavigationMenu/types.ts +56 -0
  400. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  401. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  402. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
  403. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
  404. package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
  405. package/src/components/PaceAppLayout/README.md +1 -1
  406. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  407. package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
  408. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
  409. package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
  410. package/src/components/Progress/Progress.tsx +1 -1
  411. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  412. package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
  413. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  414. package/src/components/Select/Select.tsx +95 -438
  415. package/src/components/Select/context.ts +23 -0
  416. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  417. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  418. package/src/components/Select/hooks/useSelectState.ts +104 -0
  419. package/src/components/Select/index.ts +9 -1
  420. package/src/components/Select/types.ts +123 -0
  421. package/src/components/Select/utils/text.ts +26 -0
  422. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +5 -6
  423. package/src/components/Switch/Switch.tsx +4 -4
  424. package/src/components/Table/Table.tsx +1 -1
  425. package/src/components/Tabs/Tabs.tsx +1 -1
  426. package/src/components/Textarea/Textarea.tsx +27 -29
  427. package/src/components/Toast/Toast.tsx +5 -1
  428. package/src/components/Tooltip/Tooltip.tsx +3 -3
  429. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  430. package/src/components/UserMenu/UserMenu.tsx +22 -19
  431. package/src/components/index.ts +2 -2
  432. package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
  433. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  434. package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
  435. package/src/hooks/index.ts +1 -2
  436. package/src/hooks/public/usePublicEvent.ts +5 -1
  437. package/src/hooks/public/usePublicEventLogo.ts +5 -1
  438. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  439. package/src/hooks/public/usePublicRouteParams.ts +5 -1
  440. package/src/hooks/services/useAuth.ts +32 -0
  441. package/src/hooks/services/useCurrentEvent.ts +6 -0
  442. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  443. package/src/hooks/useDataTableState.ts +8 -18
  444. package/src/hooks/useDebounce.ts +9 -0
  445. package/src/hooks/useEventTheme.ts +6 -0
  446. package/src/hooks/useFileDisplay.ts +4 -0
  447. package/src/hooks/useFileReference.ts +25 -7
  448. package/src/hooks/useFileUrl.ts +11 -1
  449. package/src/hooks/useFocusManagement.ts +16 -2
  450. package/src/hooks/useFocusTrap.ts +7 -4
  451. package/src/hooks/useFormDialog.ts +8 -7
  452. package/src/hooks/useInactivityTracker.ts +4 -1
  453. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  454. package/src/hooks/useOrganisationPermissions.ts +4 -0
  455. package/src/hooks/useOrganisationSecurity.ts +4 -0
  456. package/src/hooks/usePerformanceMonitor.ts +4 -0
  457. package/src/hooks/usePermissionCache.ts +8 -1
  458. package/src/hooks/useQueryCache.ts +12 -1
  459. package/src/hooks/useSessionRestoration.ts +4 -0
  460. package/src/hooks/useStorage.ts +4 -0
  461. package/src/hooks/useToast.ts +3 -3
  462. package/src/index.ts +2 -1
  463. package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
  464. package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
  465. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
  466. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  467. package/src/providers/services/EventServiceProvider.tsx +18 -0
  468. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  469. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  470. package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
  471. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
  472. package/src/rbac/README.md +1 -1
  473. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
  474. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  475. package/src/rbac/adapters.tsx +14 -5
  476. package/src/rbac/api.ts +100 -67
  477. package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
  478. package/src/rbac/components/NavigationGuard.tsx +1 -1
  479. package/src/rbac/components/NavigationProvider.tsx +5 -2
  480. package/src/rbac/components/PagePermissionGuard.tsx +158 -18
  481. package/src/rbac/components/PagePermissionProvider.tsx +1 -1
  482. package/src/rbac/components/PermissionEnforcer.tsx +1 -1
  483. package/src/rbac/components/RoleBasedRouter.tsx +6 -2
  484. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  485. package/src/rbac/components/SecureDataProvider.tsx +21 -6
  486. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  487. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  488. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  489. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  490. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  491. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  492. package/src/rbac/engine.ts +38 -14
  493. package/src/rbac/hooks/permissions/index.ts +7 -0
  494. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  495. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  496. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  497. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  498. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  499. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  500. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  501. package/src/rbac/hooks/useCan.test.ts +71 -64
  502. package/src/rbac/hooks/usePermissions.ts +14 -995
  503. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  504. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  505. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  506. package/src/rbac/permissions.ts +0 -30
  507. package/src/rbac/secureClient.ts +212 -61
  508. package/src/rbac/types.ts +8 -0
  509. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  510. package/src/theming/parseEventColours.ts +5 -19
  511. package/src/types/vitest-globals.d.ts +51 -26
  512. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  513. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  514. package/src/utils/__tests__/index.unit.test.ts +2 -2
  515. package/src/utils/audit/audit.ts +0 -3
  516. package/src/utils/core/cn.ts +1 -1
  517. package/src/utils/file-reference/index.ts +53 -1
  518. package/src/utils/formatting/formatting.ts +8 -18
  519. package/src/utils/index.ts +0 -1
  520. package/src/utils/security/secureDataAccess.test.ts +31 -20
  521. package/src/utils/security/secureDataAccess.ts +4 -3
  522. package/dist/chunk-6C4YBBJM.js +0 -628
  523. package/dist/chunk-6C4YBBJM.js.map +0 -1
  524. package/dist/chunk-7D4SUZUM.js 2.map +0 -1
  525. package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
  526. package/dist/chunk-7EQTDTTJ.js.map +0 -1
  527. package/dist/chunk-7FLMSG37.js 2.map +0 -1
  528. package/dist/chunk-7FLMSG37.js.map +0 -1
  529. package/dist/chunk-BC4IJKSL.js.map +0 -1
  530. package/dist/chunk-E3SPN4VZ.js +0 -12917
  531. package/dist/chunk-E3SPN4VZ.js.map +0 -1
  532. package/dist/chunk-E66EQZE6 5.js +0 -37
  533. package/dist/chunk-E66EQZE6.js 2.map +0 -1
  534. package/dist/chunk-HWIIPPNI.js.map +0 -1
  535. package/dist/chunk-I7PSE6JW 5.js +0 -191
  536. package/dist/chunk-I7PSE6JW.js 2.map +0 -1
  537. package/dist/chunk-I7PSE6JW.js.map +0 -1
  538. package/dist/chunk-IIELH4DL.js.map +0 -1
  539. package/dist/chunk-KNC55RTG.js 5.map +0 -1
  540. package/dist/chunk-KNC55RTG.js.map +0 -1
  541. package/dist/chunk-KQCRWDSA.js 5.map +0 -1
  542. package/dist/chunk-LFNCN2SP.js +0 -412
  543. package/dist/chunk-LFNCN2SP.js 2.map +0 -1
  544. package/dist/chunk-LFNCN2SP.js.map +0 -1
  545. package/dist/chunk-LMC26NLJ 2.js +0 -84
  546. package/dist/chunk-NOAYCWCX.js +0 -4993
  547. package/dist/chunk-NOAYCWCX.js.map +0 -1
  548. package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
  549. package/dist/chunk-QWWZ5CAQ.js.map +0 -1
  550. package/dist/chunk-QXHPKYJV 3.js +0 -113
  551. package/dist/chunk-R77UEZ4E.js +0 -68
  552. package/dist/chunk-R77UEZ4E.js.map +0 -1
  553. package/dist/chunk-SQGMNID3.js.map +0 -1
  554. package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
  555. package/dist/chunk-XNXXZ43G.js.map +0 -1
  556. package/dist/chunk-ZSAAAMVR 6.js +0 -25
  557. package/dist/components.js 5.map +0 -1
  558. package/dist/styles/index 2.js +0 -12
  559. package/dist/styles/index.js 5.map +0 -1
  560. package/dist/theming/runtime 5.js +0 -19
  561. package/dist/theming/runtime.js 5.map +0 -1
  562. package/docs/api/classes/ErrorBoundary.md +0 -144
  563. package/docs/migration/quick-migration-guide.md +0 -356
  564. package/docs/migration/service-architecture.md +0 -281
  565. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  566. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  567. package/src/hooks/useSecureDataAccess.ts +0 -666
  568. /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  569. /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  570. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  571. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  572. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  573. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  574. /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
  575. /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
  576. /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
  577. /package/examples/{rbac → RBAC}/index.ts +0 -0
@@ -1,13 +1,12 @@
1
1
  import {
2
2
  useAppConfig,
3
3
  useEvents,
4
- useOrganisationSecurity,
5
- useResolvedScope
6
- } from "./chunk-IIELH4DL.js";
4
+ useOrganisationSecurity
5
+ } from "./chunk-MMZ7JXPU.js";
7
6
  import {
8
7
  useOrganisations,
9
8
  useUnifiedAuth
10
- } from "./chunk-7FLMSG37.js";
9
+ } from "./chunk-EHMR7VYL.js";
11
10
  import {
12
11
  ContextValidator,
13
12
  OrganisationContextRequiredError,
@@ -18,31 +17,41 @@ import {
18
17
  isPermitted,
19
18
  isPermittedCached,
20
19
  resolveAppContext
21
- } from "./chunk-KNC55RTG.js";
20
+ } from "./chunk-24UVZUZG.js";
22
21
  import {
22
+ getCurrentAppName
23
+ } from "./chunk-F2IMUDXZ.js";
24
+ import {
25
+ createLogger,
23
26
  logger
24
27
  } from "./chunk-PWLANIRT.js";
25
28
 
26
29
  // src/rbac/secureClient.ts
27
30
  import { createClient } from "@supabase/supabase-js";
28
- var SecureSupabaseClient = class _SecureSupabaseClient {
29
- constructor(supabaseUrl, supabaseKey, organisationId, eventId, appId, isSuperAdmin = false) {
31
+ var _SecureSupabaseClient = class _SecureSupabaseClient {
32
+ constructor(supabaseUrl, supabaseKey, organisationId, eventId, appId, isSuperAdmin = false, existingClient) {
30
33
  this.edgeFunctionClient = null;
34
+ this.usesExistingClient = false;
31
35
  this.supabaseUrl = supabaseUrl;
32
36
  this.supabaseKey = supabaseKey;
33
37
  this.organisationId = organisationId;
34
38
  this.eventId = eventId;
35
39
  this.appId = appId;
36
40
  this.isSuperAdmin = isSuperAdmin;
37
- this.supabase = createClient(supabaseUrl, supabaseKey, {
38
- global: {
39
- headers: {
40
- "x-organisation-id": organisationId,
41
- "x-event-id": eventId || "",
42
- "x-app-id": appId || ""
41
+ if (existingClient) {
42
+ this.supabase = existingClient;
43
+ this.usesExistingClient = true;
44
+ } else {
45
+ this.supabase = createClient(supabaseUrl, supabaseKey, {
46
+ global: {
47
+ headers: {
48
+ "x-organisation-id": organisationId || "",
49
+ "x-event-id": eventId || "",
50
+ "x-app-id": appId || ""
51
+ }
43
52
  }
44
- }
45
- });
53
+ });
54
+ }
46
55
  this.setupContextInjection();
47
56
  this.setupEdgeFunctionHandling();
48
57
  }
@@ -52,19 +61,20 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
52
61
  setupContextInjection() {
53
62
  const originalFrom = this.supabase.from.bind(this.supabase);
54
63
  this.supabase.from = (table) => {
55
- this.validateContext();
64
+ this.validateContextForTable(table);
56
65
  const query = originalFrom(table);
57
66
  query._tableName = table;
58
67
  return this.injectContext(query, table);
59
68
  };
60
69
  const originalRpc = this.supabase.rpc.bind(this.supabase);
61
70
  this.supabase.rpc = (fn, args, options) => {
62
- this.validateContext();
71
+ this.validateContextForRpc(fn);
72
+ const safeArgs = args ?? {};
63
73
  const contextArgs = {
64
- ...args,
65
- p_organisation_id: this.organisationId,
66
- p_event_id: this.eventId,
67
- p_app_id: this.appId
74
+ ...safeArgs,
75
+ ...this.organisationId && safeArgs.p_organisation_id === void 0 ? { p_organisation_id: this.organisationId } : {},
76
+ ...this.eventId && safeArgs.p_event_id === void 0 ? { p_event_id: this.eventId } : {},
77
+ ...this.appId && safeArgs.p_app_id === void 0 ? { p_app_id: this.appId } : {}
68
78
  };
69
79
  return originalRpc(fn, contextArgs, options);
70
80
  };
@@ -79,6 +89,10 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
79
89
  * This avoids interfering with the main client's operations.
80
90
  */
81
91
  setupEdgeFunctionHandling() {
92
+ if (this.usesExistingClient) {
93
+ this.edgeFunctionClient = null;
94
+ return;
95
+ }
82
96
  this.edgeFunctionClient = createClient(this.supabaseUrl, this.supabaseKey);
83
97
  }
84
98
  /**
@@ -119,9 +133,18 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
119
133
  if (this.isSuperAdmin) {
120
134
  return originalInsert(values);
121
135
  }
136
+ if (!this.organisationId) {
137
+ throw new OrganisationContextRequiredError();
138
+ }
122
139
  const contextValues2 = Array.isArray(values) ? values.map((v) => ({ ...v, organisation_id: this.organisationId })) : { ...values, organisation_id: this.organisationId };
123
140
  return originalInsert(contextValues2);
124
141
  }
142
+ if (this.isSuperAdmin && !this.organisationId) {
143
+ return originalInsert(values);
144
+ }
145
+ if (!this.organisationId) {
146
+ throw new OrganisationContextRequiredError();
147
+ }
125
148
  const contextValues = Array.isArray(values) ? values.map((v) => ({ ...v, organisation_id: this.organisationId })) : { ...values, organisation_id: this.organisationId };
126
149
  return originalInsert(contextValues);
127
150
  };
@@ -146,6 +169,10 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
146
169
  * - Super admins: No org filter (see all users) - RLS will allow access
147
170
  * - Non-super-admins: Apply org filter as defense in depth - RLS will also filter
148
171
  *
172
+ * For system-wide tables (like core_organisations):
173
+ * - Super admins: No org filter (see all records) - RLS will allow access
174
+ * - Non-super-admins: Apply org filter as defense in depth - RLS will also filter
175
+ *
149
176
  * For other tables:
150
177
  * - Always apply org filter unless super admin bypasses it
151
178
  */
@@ -157,8 +184,31 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
157
184
  // App configuration table - no organisation scope
158
185
  "rbac_app_pages",
159
186
  // Page configuration table - scoped by app_id, not organisation_id
160
- "rbac_global_roles"
187
+ "rbac_global_roles",
161
188
  // Global roles - no organisation scope
189
+ // Person-scoped tables (organisation_id was removed in person-scoped profiles migration)
190
+ "core_person",
191
+ // Person records - person-scoped, no organisation_id
192
+ "core_member",
193
+ // Member profiles - person-scoped, no organisation_id
194
+ "core_contact",
195
+ // Contact profiles - person-scoped, no organisation_id
196
+ "core_consent",
197
+ // Consent records - person-scoped, no organisation_id
198
+ "core_identification",
199
+ // Identification records - person-scoped, no organisation_id
200
+ "core_qualification",
201
+ // Qualification records - person-scoped, no organisation_id
202
+ "medi_profile",
203
+ // Medical profiles - person-scoped, no organisation_id
204
+ "medi_condition",
205
+ // Medical conditions - person-scoped via medi_profile, no organisation_id
206
+ "medi_diet",
207
+ // Medical diets - person-scoped via medi_profile, no organisation_id
208
+ "medi_action_plan",
209
+ // Medical action plans - person-scoped via medi_profile, no organisation_id
210
+ "medi_profile_versions"
211
+ // Medical profile versions - person-scoped via medi_profile, no organisation_id
162
212
  ];
163
213
  if (tablesWithoutOrganisationId.includes(tableName)) {
164
214
  return query;
@@ -166,6 +216,13 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
166
216
  if (!this.organisationId) {
167
217
  return query;
168
218
  }
219
+ const systemWideTablesForSuperAdmins = [
220
+ "core_organisations"
221
+ // Super-admins need to see all organisations
222
+ ];
223
+ if (systemWideTablesForSuperAdmins.includes(tableName) && this.isSuperAdmin) {
224
+ return query;
225
+ }
169
226
  if (tableName === "rbac_user_profiles") {
170
227
  if (this.isSuperAdmin) {
171
228
  return query;
@@ -179,12 +236,57 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
179
236
  }
180
237
  /**
181
238
  * Validate that required context is present
239
+ * Super-admins can operate without organisation context
182
240
  */
183
241
  validateContext() {
242
+ if (this.isSuperAdmin) {
243
+ return;
244
+ }
184
245
  if (!this.organisationId) {
185
246
  throw new OrganisationContextRequiredError();
186
247
  }
187
248
  }
249
+ /**
250
+ * Determine whether a table requires organisation context.
251
+ * Tables without an organisation_id column (or global configuration tables) are safe without org context.
252
+ */
253
+ tableRequiresOrganisationContext(tableName) {
254
+ const tablesWithoutOrganisationId = /* @__PURE__ */ new Set([
255
+ "core_organisations",
256
+ "rbac_apps",
257
+ "rbac_app_pages",
258
+ "rbac_global_roles",
259
+ "core_person",
260
+ "core_member",
261
+ "core_contact",
262
+ "core_consent",
263
+ "core_identification",
264
+ "core_qualification",
265
+ "medi_profile",
266
+ "medi_condition",
267
+ "medi_diet",
268
+ "medi_action_plan",
269
+ "medi_profile_versions"
270
+ ]);
271
+ return !tablesWithoutOrganisationId.has(tableName);
272
+ }
273
+ /**
274
+ * Validate context for a specific table operation.
275
+ */
276
+ validateContextForTable(tableName) {
277
+ if (this.isSuperAdmin) return;
278
+ if (!this.organisationId && this.tableRequiresOrganisationContext(tableName)) {
279
+ throw new OrganisationContextRequiredError();
280
+ }
281
+ }
282
+ /**
283
+ * Validate context for a specific RPC call.
284
+ */
285
+ validateContextForRpc(fn) {
286
+ if (this.isSuperAdmin) return;
287
+ if (_SecureSupabaseClient.GLOBAL_RPC_ALLOWLIST.has(fn)) return;
288
+ this.validateContext();
289
+ }
188
290
  /**
189
291
  * Get the current organisation ID
190
292
  */
@@ -210,7 +312,7 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
210
312
  return new _SecureSupabaseClient(
211
313
  this.supabaseUrl,
212
314
  this.supabaseKey,
213
- updates.organisationId || this.organisationId,
315
+ updates.organisationId !== void 0 ? updates.organisationId : this.organisationId,
214
316
  updates.eventId !== void 0 ? updates.eventId : this.eventId,
215
317
  updates.appId !== void 0 ? updates.appId : this.appId,
216
318
  updates.isSuperAdmin !== void 0 ? updates.isSuperAdmin : this.isSuperAdmin
@@ -231,15 +333,216 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
231
333
  });
232
334
  }
233
335
  };
336
+ /**
337
+ * RPC functions that are safe to call without organisation context.
338
+ *
339
+ * These functions must:
340
+ * - rely on JWT context (auth.uid()) for authentication
341
+ * - not read or write organisation-scoped data
342
+ *
343
+ * This allowlist enables compliant consuming apps to use `secureSupabase.rpc(...)`
344
+ * even before an organisation is selected (common during initial page load/refresh).
345
+ */
346
+ _SecureSupabaseClient.GLOBAL_RPC_ALLOWLIST = /* @__PURE__ */ new Set([
347
+ "data_rbac_apps_list"
348
+ ]);
349
+ var SecureSupabaseClient = _SecureSupabaseClient;
234
350
  function createSecureClient(supabaseUrl, supabaseKey, organisationId, eventId, appId, isSuperAdmin = false) {
235
351
  return new SecureSupabaseClient(supabaseUrl, supabaseKey, organisationId, eventId, appId, isSuperAdmin);
236
352
  }
237
- function fromSupabaseClient(client, organisationId, eventId, appId) {
238
- throw new Error("fromSupabaseClient is not supported. Use createSecureClient instead.");
353
+ function fromSupabaseClient(client, organisationId, eventId, appId, isSuperAdmin = false) {
354
+ return new SecureSupabaseClient("", "", organisationId, eventId, appId, isSuperAdmin, client);
355
+ }
356
+
357
+ // src/rbac/hooks/useResolvedScope.ts
358
+ import { useEffect, useState, useRef, useMemo } from "react";
359
+ var log = createLogger("useResolvedScope");
360
+ var appConfigCache = /* @__PURE__ */ new Map();
361
+ var CACHE_TTL = 5 * 60 * 1e3;
362
+ function useResolvedScope({
363
+ supabase,
364
+ selectedOrganisationId,
365
+ selectedEventId
366
+ }) {
367
+ const [resolvedScope, setResolvedScope] = useState(null);
368
+ const [isLoading, setIsLoading] = useState(true);
369
+ const [error, setError] = useState(null);
370
+ const stableScopeRef = useRef({
371
+ organisationId: "",
372
+ appId: "",
373
+ eventId: void 0
374
+ });
375
+ useEffect(() => {
376
+ if (resolvedScope) {
377
+ const newScope = {
378
+ organisationId: resolvedScope.organisationId || "",
379
+ appId: resolvedScope.appId || "",
380
+ eventId: resolvedScope.eventId
381
+ };
382
+ if (stableScopeRef.current.organisationId !== newScope.organisationId || stableScopeRef.current.eventId !== newScope.eventId || stableScopeRef.current.appId !== newScope.appId) {
383
+ stableScopeRef.current = {
384
+ organisationId: newScope.organisationId,
385
+ appId: newScope.appId,
386
+ eventId: newScope.eventId
387
+ };
388
+ }
389
+ } else {
390
+ stableScopeRef.current = { organisationId: "", appId: "", eventId: void 0 };
391
+ }
392
+ }, [resolvedScope]);
393
+ const appName = getCurrentAppName();
394
+ const stableScope = stableScopeRef.current;
395
+ useEffect(() => {
396
+ let cancelled = false;
397
+ const resolveScope = async () => {
398
+ if (!supabase && !selectedOrganisationId && !selectedEventId) {
399
+ if (!cancelled) {
400
+ setResolvedScope(null);
401
+ setIsLoading(false);
402
+ setError(null);
403
+ }
404
+ return;
405
+ }
406
+ setIsLoading(true);
407
+ setError(null);
408
+ try {
409
+ const appName2 = getCurrentAppName();
410
+ let appId = void 0;
411
+ let appConfig = null;
412
+ if (supabase && appName2) {
413
+ try {
414
+ const { data: session } = await supabase.auth.getSession();
415
+ if (!session?.session) {
416
+ log.debug(`Skipping app resolution for "${appName2}" - user not authenticated`);
417
+ } else {
418
+ const cached = appConfigCache.get(appName2);
419
+ const now = Date.now();
420
+ if (cached && now - cached.timestamp < CACHE_TTL) {
421
+ appId = cached.appId;
422
+ appConfig = cached.appConfig;
423
+ } else {
424
+ const { data: app, error: error2 } = await supabase.from("rbac_apps").select("id, name, requires_event, is_active").eq("name", appName2).eq("is_active", true).single();
425
+ if (error2) {
426
+ if (error2.code === "406" || error2.code === "PGRST116" || error2.message?.includes("406")) {
427
+ log.debug(`App resolution blocked by RLS for "${appName2}" - user may not be authenticated`);
428
+ appId = void 0;
429
+ } else {
430
+ const { data: inactiveApp } = await supabase.from("rbac_apps").select("id, name, is_active").eq("name", appName2).single();
431
+ if (inactiveApp) {
432
+ log.error(`App "${appName2}" exists but is inactive (is_active: ${inactiveApp.is_active})`);
433
+ appId = void 0;
434
+ } else {
435
+ log.error(`App "${appName2}" not found in rbac_apps table`, { error: error2 });
436
+ appId = void 0;
437
+ }
438
+ }
439
+ } else if (app) {
440
+ appId = app.id;
441
+ appConfig = { requires_event: app.requires_event ?? false };
442
+ appConfigCache.set(appName2, { appId, appConfig, timestamp: now });
443
+ }
444
+ }
445
+ }
446
+ } catch (error2) {
447
+ const errorMessage = error2 instanceof Error ? error2.message : String(error2);
448
+ if (!errorMessage.includes("406") && !errorMessage.includes("PGRST116")) {
449
+ log.error("Unexpected error resolving app config:", error2);
450
+ } else {
451
+ log.debug("App resolution skipped - authentication required");
452
+ }
453
+ }
454
+ }
455
+ const initialScope = {
456
+ organisationId: appConfig?.requires_event ? void 0 : selectedOrganisationId || void 0,
457
+ eventId: selectedEventId || void 0,
458
+ appId
459
+ };
460
+ const validation = await ContextValidator.resolveRequiredContext(
461
+ initialScope,
462
+ appConfig,
463
+ appName2 || void 0,
464
+ supabase
465
+ );
466
+ if (!validation.isValid) {
467
+ if (appName2 === "PORTAL" || appName2 === "ADMIN") {
468
+ if (!cancelled) {
469
+ const optionalContextScope = {
470
+ organisationId: void 0,
471
+ eventId: void 0,
472
+ appId: appId || void 0
473
+ // appId might be undefined if query failed, that's OK
474
+ };
475
+ setResolvedScope(optionalContextScope);
476
+ setError(null);
477
+ setIsLoading(false);
478
+ }
479
+ return;
480
+ }
481
+ if (appConfig?.requires_event && selectedEventId) {
482
+ if (!cancelled) {
483
+ const eventScope = {
484
+ organisationId: void 0,
485
+ // Will be derived from event during permission check
486
+ eventId: selectedEventId,
487
+ appId: appId || void 0
488
+ };
489
+ setResolvedScope(eventScope);
490
+ setError(null);
491
+ setIsLoading(false);
492
+ }
493
+ return;
494
+ }
495
+ if (!cancelled) {
496
+ setResolvedScope(null);
497
+ setError(validation.error || new Error("Context validation failed"));
498
+ setIsLoading(false);
499
+ }
500
+ return;
501
+ }
502
+ if (!cancelled) {
503
+ setResolvedScope(validation.resolvedScope);
504
+ setError(null);
505
+ setIsLoading(false);
506
+ }
507
+ } catch (err) {
508
+ if (!cancelled) {
509
+ setError(err);
510
+ setIsLoading(false);
511
+ }
512
+ }
513
+ };
514
+ resolveScope();
515
+ return () => {
516
+ cancelled = true;
517
+ };
518
+ }, [selectedOrganisationId, selectedEventId, supabase]);
519
+ const allowsOptionalContexts = appName === "PORTAL" || appName === "ADMIN";
520
+ const hasValidScope = allowsOptionalContexts ? true : stableScope.appId || stableScope.organisationId;
521
+ const finalScope = useMemo(() => {
522
+ if (!hasValidScope) {
523
+ return allowsOptionalContexts ? {} : null;
524
+ }
525
+ const scope = {};
526
+ if (stableScope.organisationId) {
527
+ scope.organisationId = stableScope.organisationId;
528
+ }
529
+ if (stableScope.eventId) {
530
+ scope.eventId = stableScope.eventId;
531
+ }
532
+ if (stableScope.appId) {
533
+ scope.appId = stableScope.appId;
534
+ }
535
+ return scope;
536
+ }, [hasValidScope, allowsOptionalContexts, stableScope.organisationId, stableScope.eventId, stableScope.appId]);
537
+ return {
538
+ resolvedScope: finalScope,
539
+ isLoading,
540
+ error
541
+ };
239
542
  }
240
543
 
241
544
  // src/rbac/hooks/useRBAC.ts
242
- import { useState, useEffect, useCallback, useMemo } from "react";
545
+ import { useState as useState2, useEffect as useEffect2, useCallback, useMemo as useMemo2 } from "react";
243
546
  function mapAccessLevelToEventRole(level) {
244
547
  switch (level) {
245
548
  case "viewer":
@@ -270,13 +573,13 @@ function useRBAC(pageId) {
270
573
  selectedEvent,
271
574
  eventLoading
272
575
  } = useUnifiedAuth();
273
- const [globalRole, setGlobalRole] = useState(null);
274
- const [organisationRole, setOrganisationRole] = useState(null);
275
- const [eventAppRole, setEventAppRole] = useState(null);
276
- const [permissionMap, setPermissionMap] = useState({});
277
- const [currentScope, setCurrentScope] = useState(null);
278
- const [isLoading, setIsLoading] = useState(false);
279
- const [error, setError] = useState(null);
576
+ const [globalRole, setGlobalRole] = useState2(null);
577
+ const [organisationRole, setOrganisationRole] = useState2(null);
578
+ const [eventAppRole, setEventAppRole] = useState2(null);
579
+ const [permissionMap, setPermissionMap] = useState2({});
580
+ const [currentScope, setCurrentScope] = useState2(null);
581
+ const [isLoading, setIsLoading] = useState2(false);
582
+ const [error, setError] = useState2(null);
280
583
  const resetState = useCallback(() => {
281
584
  setGlobalRole(null);
282
585
  setOrganisationRole(null);
@@ -316,7 +619,7 @@ function useRBAC(pageId) {
316
619
  if (!resolved) {
317
620
  if (appName === "PORTAL" || appName === "ADMIN") {
318
621
  try {
319
- const { getAppConfigByName } = await import("./api-N774RPUA.js");
622
+ const { getAppConfigByName } = await import("./api-MVVQZLJI.js");
320
623
  await getAppConfigByName(appName);
321
624
  } catch (err) {
322
625
  }
@@ -401,12 +704,12 @@ function useRBAC(pageId) {
401
704
  },
402
705
  [globalRole, organisationRole, permissionMap]
403
706
  );
404
- const isSuperAdmin = useMemo(() => globalRole === "super_admin" || permissionMap["*"] === true, [globalRole, permissionMap]);
405
- const isOrgAdmin = useMemo(() => organisationRole === "org_admin" || isSuperAdmin, [organisationRole, isSuperAdmin]);
406
- const isEventAdmin = useMemo(() => eventAppRole === "event_admin" || isSuperAdmin, [eventAppRole, isSuperAdmin]);
407
- const canManageOrganisation = useMemo(() => isSuperAdmin || organisationRole === "org_admin", [isSuperAdmin, organisationRole]);
408
- const canManageEvent = useMemo(() => isSuperAdmin || eventAppRole === "event_admin", [isSuperAdmin, eventAppRole]);
409
- useEffect(() => {
707
+ const isSuperAdmin = useMemo2(() => globalRole === "super_admin" || permissionMap["*"] === true, [globalRole, permissionMap]);
708
+ const isOrgAdmin = useMemo2(() => organisationRole === "org_admin" || isSuperAdmin, [organisationRole, isSuperAdmin]);
709
+ const isEventAdmin = useMemo2(() => eventAppRole === "event_admin" || isSuperAdmin, [eventAppRole, isSuperAdmin]);
710
+ const canManageOrganisation = useMemo2(() => isSuperAdmin || organisationRole === "org_admin", [isSuperAdmin, organisationRole]);
711
+ const canManageEvent = useMemo2(() => isSuperAdmin || eventAppRole === "event_admin", [isSuperAdmin, eventAppRole]);
712
+ useEffect2(() => {
410
713
  loadRBACContext();
411
714
  }, [loadRBACContext, appName, appConfig, eventLoading, selectedEvent?.event_id, user, session, selectedOrganisation?.id, orgContextReady, orgLoading]);
412
715
  return {
@@ -425,197 +728,176 @@ function useRBAC(pageId) {
425
728
  };
426
729
  }
427
730
 
428
- // src/rbac/hooks/usePermissions.ts
429
- import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useMemo as useMemo2, useRef } from "react";
430
-
431
- // src/rbac/utils/deep-equal.ts
432
- function scopeEqual(a, b) {
433
- if (a === b) {
434
- return true;
435
- }
436
- if (a == null || b == null) {
437
- return a === b;
731
+ // src/rbac/hooks/permissions/useAccessLevel.ts
732
+ import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
733
+ function useAccessLevel(userId, scope) {
734
+ const [accessLevel, setAccessLevel] = useState3("viewer");
735
+ const [isLoading, setIsLoading] = useState3(true);
736
+ const [error, setError] = useState3(null);
737
+ let appName;
738
+ try {
739
+ const { appName: contextAppName } = useAppConfig();
740
+ appName = contextAppName;
741
+ } catch {
438
742
  }
439
- return a.organisationId === b.organisationId && a.eventId === b.eventId && a.appId === b.appId;
440
- }
441
-
442
- // src/rbac/hooks/usePermissions.ts
443
- function usePermissions(userId, organisationId, eventId, appId) {
444
- const [permissions, setPermissions] = useState2({});
445
- const [isLoading, setIsLoading] = useState2(true);
446
- const [error, setError] = useState2(null);
447
- const [fetchTrigger, setFetchTrigger] = useState2(0);
448
- const isFetchingRef = useRef(false);
449
- const logger2 = getRBACLogger();
450
- const prevValuesRef = useRef({ userId, organisationId, eventId, appId });
451
- const orgId = organisationId || "";
452
- useEffect2(() => {
743
+ const fetchAccessLevel = useCallback2(async () => {
453
744
  if (!userId) {
745
+ setAccessLevel("viewer");
746
+ setIsLoading(false);
454
747
  return;
455
748
  }
456
- if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
457
- const timeoutId = setTimeout(() => {
458
- setError(new Error("Organisation context is required for permission checks"));
459
- setIsLoading(false);
460
- }, 3e3);
461
- return () => clearTimeout(timeoutId);
462
- }
463
- if (error?.message === "Organisation context is required for permission checks") {
749
+ try {
750
+ setIsLoading(true);
464
751
  setError(null);
465
- }
466
- }, [userId, organisationId, error, orgId]);
467
- useEffect2(() => {
468
- const paramsChanged = prevValuesRef.current.userId !== userId || prevValuesRef.current.organisationId !== organisationId || prevValuesRef.current.eventId !== eventId || prevValuesRef.current.appId !== appId;
469
- if (paramsChanged) {
470
- if (prevValuesRef.current.appId !== appId) {
471
- }
472
- prevValuesRef.current = { userId, organisationId, eventId, appId };
473
- setFetchTrigger((prev) => prev + 1);
474
- }
475
- }, [userId, organisationId, eventId, appId, logger2]);
476
- useEffect2(() => {
477
- const fetchPermissions = async () => {
478
- if (isFetchingRef.current) {
479
- return;
480
- }
481
- if (!userId) {
482
- setPermissions({});
752
+ const { isSuperAdmin: checkSuperAdmin } = await import("./api-MVVQZLJI.js");
753
+ const isSuperAdminUser = await checkSuperAdmin(userId);
754
+ if (isSuperAdminUser) {
755
+ setAccessLevel("super");
483
756
  setIsLoading(false);
484
757
  return;
485
758
  }
486
- if (!userId) {
487
- setPermissions({});
759
+ if (appName !== "PORTAL" && appName !== "ADMIN" && !scope.organisationId && !scope.eventId) {
760
+ const orgError = new OrganisationContextRequiredError();
761
+ setError(orgError);
762
+ setAccessLevel("viewer");
488
763
  setIsLoading(false);
489
764
  return;
490
765
  }
491
- if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
492
- setIsLoading(true);
493
- setError(null);
494
- return;
495
- }
496
- try {
497
- isFetchingRef.current = true;
498
- setIsLoading(true);
499
- setError(null);
500
- const scope = {
501
- organisationId: orgId,
502
- eventId,
503
- appId
504
- };
505
- const permissionMap = await getPermissionMap({ userId, scope });
506
- const permissionCount = Object.keys(permissionMap).length;
507
- if (permissionCount === 0 && Object.keys(permissions).length > 0) {
508
- logger2.warn("[usePermissions] Permissions fetched but returned empty map", {
509
- scope: { organisationId: orgId, eventId, appId }
510
- });
511
- }
512
- setPermissions(permissionMap);
513
- } catch (err) {
514
- logger2.error("[usePermissions] Failed to fetch permissions:", err);
515
- setError(err instanceof Error ? err : new Error("Failed to fetch permissions"));
516
- } finally {
517
- setIsLoading(false);
518
- isFetchingRef.current = false;
519
- }
520
- };
521
- fetchPermissions();
522
- }, [fetchTrigger, userId, organisationId, eventId, appId]);
523
- const hasPermission = useCallback2((permission) => {
524
- if (permissions["*"]) {
525
- return true;
526
- }
527
- return permissions[permission] === true;
528
- }, [permissions]);
529
- const hasAnyPermission = useCallback2((permissionList) => {
530
- if (permissions["*"]) {
531
- return true;
532
- }
533
- return permissionList.some((p) => permissions[p] === true);
534
- }, [permissions]);
535
- const hasAllPermissions = useCallback2((permissionList) => {
536
- if (permissions["*"]) {
537
- return true;
538
- }
539
- return permissionList.every((p) => permissions[p] === true);
540
- }, [permissions]);
541
- const refetch = useCallback2(async () => {
542
- if (isFetchingRef.current) {
543
- return;
766
+ const level = await getAccessLevel({ userId, scope }, null, appName);
767
+ setAccessLevel(level);
768
+ } catch (err) {
769
+ const error2 = err instanceof Error ? err : new Error("Failed to fetch access level");
770
+ setError(error2);
771
+ setAccessLevel("viewer");
772
+ } finally {
773
+ setIsLoading(false);
544
774
  }
775
+ }, [userId, scope.organisationId, scope.eventId, scope.appId, appName]);
776
+ useEffect3(() => {
777
+ fetchAccessLevel();
778
+ }, [fetchAccessLevel]);
779
+ return useMemo3(() => ({
780
+ accessLevel,
781
+ isLoading,
782
+ error,
783
+ refetch: fetchAccessLevel
784
+ }), [accessLevel, isLoading, error, fetchAccessLevel]);
785
+ }
786
+
787
+ // src/rbac/hooks/permissions/useCachedPermissions.ts
788
+ import { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo4, useState as useState4 } from "react";
789
+ function useCachedPermissions(userId, scope) {
790
+ const [permissions, setPermissions] = useState4({});
791
+ const [isLoading, setIsLoading] = useState4(true);
792
+ const [error, setError] = useState4(null);
793
+ const fetchCachedPermissions = useCallback3(async () => {
545
794
  if (!userId) {
546
795
  setPermissions({});
547
796
  setIsLoading(false);
548
797
  return;
549
798
  }
550
- if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
551
- setIsLoading(true);
552
- setError(null);
553
- return;
554
- }
555
799
  try {
556
- isFetchingRef.current = true;
557
800
  setIsLoading(true);
558
801
  setError(null);
559
- const scope = {
560
- organisationId: orgId,
561
- eventId,
562
- appId
563
- };
564
802
  const permissionMap = await getPermissionMap({ userId, scope });
565
803
  setPermissions(permissionMap);
566
804
  } catch (err) {
567
- const logger3 = getRBACLogger();
568
- logger3.error("Failed to refetch permissions:", err);
569
- setError(err instanceof Error ? err : new Error("Failed to fetch permissions"));
805
+ setError(err instanceof Error ? err : new Error("Failed to fetch cached permissions"));
570
806
  } finally {
571
807
  setIsLoading(false);
572
- isFetchingRef.current = false;
573
808
  }
574
- }, [userId, organisationId, eventId, appId]);
575
- return useMemo2(() => ({
809
+ }, [userId, scope.organisationId, scope.eventId, scope.appId]);
810
+ const invalidateCache = useCallback3(() => {
811
+ fetchCachedPermissions();
812
+ }, [fetchCachedPermissions]);
813
+ useEffect4(() => {
814
+ fetchCachedPermissions();
815
+ }, [fetchCachedPermissions]);
816
+ return useMemo4(() => ({
576
817
  permissions,
577
818
  isLoading,
578
819
  error,
579
- hasPermission,
580
- hasAnyPermission,
581
- hasAllPermissions,
582
- refetch
583
- }), [permissions, isLoading, error, hasPermission, hasAnyPermission, hasAllPermissions, refetch]);
820
+ invalidateCache,
821
+ refetch: fetchCachedPermissions
822
+ }), [permissions, isLoading, error, invalidateCache, fetchCachedPermissions]);
584
823
  }
585
- function useCan(userId, scope, permission, pageId, useCache = true, appName) {
586
- const [can, setCan] = useState2(false);
587
- const [isLoading, setIsLoading] = useState2(true);
588
- const [error, setError] = useState2(null);
589
- const [isSuperAdmin, setIsSuperAdmin] = useState2(null);
824
+
825
+ // src/rbac/hooks/permissions/useCan.ts
826
+ import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo5, useRef as useRef2, useState as useState5 } from "react";
827
+
828
+ // src/rbac/utils/deep-equal.ts
829
+ function scopeEqual(a, b) {
830
+ if (a === b) {
831
+ return true;
832
+ }
833
+ if (a == null || b == null) {
834
+ return a === b;
835
+ }
836
+ return a.organisationId === b.organisationId && a.eventId === b.eventId && a.appId === b.appId;
837
+ }
838
+
839
+ // src/rbac/hooks/permissions/useCan.ts
840
+ function useCan(userId, scope, permission, pageId, useCache = true, precomputedSuperAdmin = null, appName) {
841
+ const [can, setCan] = useState5(false);
842
+ const [isLoading, setIsLoading] = useState5(true);
843
+ const [error, setError] = useState5(null);
844
+ const [isSuperAdmin, setIsSuperAdmin] = useState5(precomputedSuperAdmin ?? null);
590
845
  const isValidScope = scope && typeof scope === "object";
591
846
  const organisationId = isValidScope ? scope.organisationId : void 0;
592
847
  const eventId = isValidScope ? scope.eventId : void 0;
593
848
  const appId = isValidScope ? scope.appId : void 0;
594
- useEffect2(() => {
595
- if (!userId) {
596
- setIsSuperAdmin(false);
597
- return;
598
- }
599
- let cancelled = false;
600
- const checkSuperAdmin = async () => {
601
- try {
602
- const { isSuperAdmin: checkSuperAdmin2 } = await import("./api-N774RPUA.js");
603
- const isSuper = await checkSuperAdmin2(userId);
604
- if (!cancelled) {
605
- setIsSuperAdmin(isSuper);
606
- }
607
- } catch (err) {
608
- if (!cancelled) {
609
- setIsSuperAdmin(false);
610
- }
849
+ useEffect5(() => {
850
+ if (precomputedSuperAdmin === null) {
851
+ if (!userId) {
852
+ setIsSuperAdmin(false);
853
+ return;
611
854
  }
612
- };
613
- checkSuperAdmin();
614
- return () => {
615
- cancelled = true;
616
- };
617
- }, [userId]);
618
- useEffect2(() => {
855
+ let cancelled = false;
856
+ const checkSuperAdmin = async () => {
857
+ const startTime = Date.now();
858
+ try {
859
+ const { isSuperAdmin: checkSuperAdmin2 } = await import("./api-MVVQZLJI.js");
860
+ const timeoutWarning = setTimeout(() => {
861
+ if (!cancelled) {
862
+ console.warn("[useCan] Super admin check taking longer than 5 seconds", {
863
+ userId,
864
+ elapsedMs: Date.now() - startTime
865
+ });
866
+ }
867
+ }, 5e3);
868
+ const isSuper = await checkSuperAdmin2(userId);
869
+ clearTimeout(timeoutWarning);
870
+ if (!cancelled) {
871
+ const elapsed = Date.now() - startTime;
872
+ if (elapsed > 1e3) {
873
+ console.warn("[useCan] Super admin check took longer than expected", {
874
+ userId,
875
+ elapsedMs: elapsed
876
+ });
877
+ }
878
+ setIsSuperAdmin(isSuper);
879
+ }
880
+ } catch (err) {
881
+ if (!cancelled) {
882
+ const elapsed = Date.now() - startTime;
883
+ console.error("[useCan] Error checking super admin", {
884
+ userId,
885
+ error: err,
886
+ elapsedMs: elapsed
887
+ });
888
+ setIsSuperAdmin(false);
889
+ }
890
+ }
891
+ };
892
+ checkSuperAdmin();
893
+ return () => {
894
+ cancelled = true;
895
+ };
896
+ } else {
897
+ setIsSuperAdmin(precomputedSuperAdmin);
898
+ }
899
+ }, [userId, precomputedSuperAdmin]);
900
+ useEffect5(() => {
619
901
  const isPagePermission = permission.includes(":page.") || !!pageId;
620
902
  const requiresOrgId = !isPagePermission;
621
903
  if (isSuperAdmin === true) {
@@ -633,12 +915,12 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
633
915
  setError(null);
634
916
  }
635
917
  }, [isValidScope, organisationId, error, permission, pageId, isSuperAdmin]);
636
- const lastUserIdRef = useRef(null);
637
- const lastScopeRef = useRef(null);
638
- const lastPermissionRef = useRef(null);
639
- const lastPageIdRef = useRef(null);
640
- const lastUseCacheRef = useRef(null);
641
- const stableScope = useMemo2(() => {
918
+ const lastUserIdRef = useRef2(null);
919
+ const lastScopeRef = useRef2(null);
920
+ const lastPermissionRef = useRef2(null);
921
+ const lastPageIdRef = useRef2(null);
922
+ const lastUseCacheRef = useRef2(null);
923
+ const stableScope = useMemo5(() => {
642
924
  if (!isValidScope) {
643
925
  return null;
644
926
  }
@@ -648,8 +930,8 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
648
930
  appId
649
931
  };
650
932
  }, [isValidScope, organisationId, eventId, appId]);
651
- const prevScopeRef = useRef(null);
652
- useEffect2(() => {
933
+ const prevScopeRef = useRef2(null);
934
+ useEffect5(() => {
653
935
  const scopeChanged = !scopeEqual(prevScopeRef.current, stableScope);
654
936
  if (lastUserIdRef.current !== userId || scopeChanged || lastPermissionRef.current !== permission || lastPageIdRef.current !== pageId || lastUseCacheRef.current !== useCache) {
655
937
  lastUserIdRef.current = userId;
@@ -660,7 +942,19 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
660
942
  const checkPermission = async () => {
661
943
  if (!userId) {
662
944
  setCan(false);
663
- setIsLoading(false);
945
+ setIsLoading(false);
946
+ return;
947
+ }
948
+ if (isSuperAdmin === true) {
949
+ setCan(true);
950
+ setIsLoading(false);
951
+ setError(null);
952
+ return;
953
+ }
954
+ if (isSuperAdmin === null) {
955
+ setIsLoading(true);
956
+ setCan(false);
957
+ setError(null);
664
958
  return;
665
959
  }
666
960
  if (!isValidScope) {
@@ -674,13 +968,10 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
674
968
  const isPageName = pageId && typeof pageId === "string" && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(pageId);
675
969
  const needsAppIdForPageName = isPagePermission && isPageName;
676
970
  if (requiresOrgId && (!organisationId || organisationId === null || typeof organisationId === "string" && organisationId.trim() === "")) {
677
- if (isSuperAdmin === true) {
678
- } else {
679
- setIsLoading(true);
680
- setCan(false);
681
- setError(null);
682
- return;
683
- }
971
+ setIsLoading(true);
972
+ setCan(false);
973
+ setError(null);
974
+ return;
684
975
  }
685
976
  if (needsAppIdForPageName && (!appId || appId === null || typeof appId === "string" && appId.trim() === "")) {
686
977
  setIsLoading(true);
@@ -696,11 +987,12 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
696
987
  ...eventId ? { eventId } : {},
697
988
  ...appId ? { appId } : {}
698
989
  };
699
- const result = useCache ? await isPermittedCached({ userId, scope: validScope, permission, pageId }, void 0, appName) : await isPermitted({ userId, scope: validScope, permission, pageId }, void 0, appName);
990
+ const result = useCache ? await isPermittedCached({ userId, scope: validScope, permission, pageId }, void 0, appName) : await isPermitted({ userId, scope: validScope, permission, pageId }, void 0, appName, isSuperAdmin === false ? false : null);
700
991
  setCan(result);
701
992
  } catch (err) {
702
993
  const logger2 = getRBACLogger();
703
994
  logger2.error("Permission check error:", { permission, error: err });
995
+ console.error("[useCan] Permission check error", { userId, permission, error: err });
704
996
  setError(err instanceof Error ? err : new Error("Failed to check permission"));
705
997
  setCan(false);
706
998
  } finally {
@@ -710,7 +1002,7 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
710
1002
  checkPermission();
711
1003
  }
712
1004
  }, [userId, stableScope, permission, pageId, useCache, appName, isSuperAdmin]);
713
- const refetch = useCallback2(async () => {
1005
+ const refetch = useCallback4(async () => {
714
1006
  if (!userId) {
715
1007
  setCan(false);
716
1008
  setIsLoading(false);
@@ -738,7 +1030,7 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
738
1030
  ...eventId ? { eventId } : {},
739
1031
  ...appId ? { appId } : {}
740
1032
  };
741
- const result = useCache ? await isPermittedCached({ userId, scope: validScope, permission, pageId }, void 0, appName) : await isPermitted({ userId, scope: validScope, permission, pageId }, void 0, appName);
1033
+ const result = useCache ? await isPermittedCached({ userId, scope: validScope, permission, pageId }, void 0, appName) : await isPermitted({ userId, scope: validScope, permission, pageId }, void 0, appName, null);
742
1034
  setCan(result);
743
1035
  } catch (err) {
744
1036
  setError(err instanceof Error ? err : new Error("Failed to check permission"));
@@ -747,107 +1039,63 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
747
1039
  setIsLoading(false);
748
1040
  }
749
1041
  }, [userId, isValidScope, organisationId, eventId, appId, permission, pageId, useCache, appName]);
750
- return useMemo2(() => ({
1042
+ return useMemo5(() => ({
751
1043
  can,
752
1044
  isLoading,
753
1045
  error,
754
1046
  refetch
755
1047
  }), [can, isLoading, error, refetch]);
756
1048
  }
757
- function useAccessLevel(userId, scope) {
758
- const [accessLevel, setAccessLevel] = useState2("viewer");
759
- const [isLoading, setIsLoading] = useState2(true);
760
- const [error, setError] = useState2(null);
761
- let appName;
762
- try {
763
- const { appName: contextAppName } = useAppConfig();
764
- appName = contextAppName;
765
- } catch {
766
- }
767
- const fetchAccessLevel = useCallback2(async () => {
768
- if (!userId) {
769
- setAccessLevel("viewer");
770
- setIsLoading(false);
771
- return;
772
- }
773
- try {
774
- setIsLoading(true);
775
- setError(null);
776
- const { isSuperAdmin: checkSuperAdmin } = await import("./api-N774RPUA.js");
777
- const isSuperAdminUser = await checkSuperAdmin(userId);
778
- if (isSuperAdminUser) {
779
- setAccessLevel("super");
780
- setIsLoading(false);
781
- return;
782
- }
783
- if (appName !== "PORTAL" && appName !== "ADMIN" && !scope.organisationId && !scope.eventId) {
784
- const orgError = new OrganisationContextRequiredError();
785
- setError(orgError);
786
- setAccessLevel("viewer");
787
- setIsLoading(false);
788
- return;
789
- }
790
- const level = await getAccessLevel({ userId, scope }, null, appName);
791
- setAccessLevel(level);
792
- } catch (err) {
793
- const error2 = err instanceof Error ? err : new Error("Failed to fetch access level");
794
- setError(error2);
795
- setAccessLevel("viewer");
796
- } finally {
797
- setIsLoading(false);
798
- }
799
- }, [userId, scope.organisationId, scope.eventId, scope.appId, appName]);
800
- useEffect2(() => {
801
- fetchAccessLevel();
802
- }, [fetchAccessLevel]);
803
- return useMemo2(() => ({
804
- accessLevel,
805
- isLoading,
806
- error,
807
- refetch: fetchAccessLevel
808
- }), [accessLevel, isLoading, error, fetchAccessLevel]);
809
- }
810
- function useMultiplePermissions(userId, scope, permissions, useCache = true) {
811
- const [results, setResults] = useState2({});
812
- const [isLoading, setIsLoading] = useState2(true);
813
- const [error, setError] = useState2(null);
814
- const checkPermissions = useCallback2(async () => {
1049
+
1050
+ // src/rbac/hooks/permissions/useHasAllPermissions.ts
1051
+ import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo6, useState as useState6 } from "react";
1052
+ function useHasAllPermissions(userId, scope, permissions, useCache = true) {
1053
+ const [hasAll, setHasAll] = useState6(false);
1054
+ const [isLoading, setIsLoading] = useState6(true);
1055
+ const [error, setError] = useState6(null);
1056
+ const checkAllPermissions = useCallback5(async () => {
815
1057
  if (!userId || permissions.length === 0) {
816
- setResults({});
1058
+ setHasAll(false);
817
1059
  setIsLoading(false);
818
1060
  return;
819
1061
  }
820
1062
  try {
821
1063
  setIsLoading(true);
822
1064
  setError(null);
823
- const permissionResults = {};
1065
+ let hasAllPermissions = true;
824
1066
  for (const permission of permissions) {
825
- const result = useCache ? await isPermittedCached({ userId, scope, permission }) : await isPermitted({ userId, scope, permission });
826
- permissionResults[permission] = result;
1067
+ const result = useCache ? await isPermittedCached({ userId, scope, permission }) : await isPermitted({ userId, scope, permission }, null, void 0, null);
1068
+ if (!result) {
1069
+ hasAllPermissions = false;
1070
+ break;
1071
+ }
827
1072
  }
828
- setResults(permissionResults);
1073
+ setHasAll(hasAllPermissions);
829
1074
  } catch (err) {
830
1075
  setError(err instanceof Error ? err : new Error("Failed to check permissions"));
831
- setResults({});
1076
+ setHasAll(false);
832
1077
  } finally {
833
1078
  setIsLoading(false);
834
1079
  }
835
1080
  }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);
836
- useEffect2(() => {
837
- checkPermissions();
838
- }, [checkPermissions]);
839
- return useMemo2(() => ({
840
- results,
1081
+ useEffect6(() => {
1082
+ checkAllPermissions();
1083
+ }, [checkAllPermissions]);
1084
+ return useMemo6(() => ({
1085
+ hasAll,
841
1086
  isLoading,
842
1087
  error,
843
- refetch: checkPermissions
844
- }), [results, isLoading, error, checkPermissions]);
1088
+ refetch: checkAllPermissions
1089
+ }), [hasAll, isLoading, error, checkAllPermissions]);
845
1090
  }
1091
+
1092
+ // src/rbac/hooks/permissions/useHasAnyPermission.ts
1093
+ import { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo7, useState as useState7 } from "react";
846
1094
  function useHasAnyPermission(userId, scope, permissions, useCache = true) {
847
- const [hasAny, setHasAny] = useState2(false);
848
- const [isLoading, setIsLoading] = useState2(true);
849
- const [error, setError] = useState2(null);
850
- const checkAnyPermission = useCallback2(async () => {
1095
+ const [hasAny, setHasAny] = useState7(false);
1096
+ const [isLoading, setIsLoading] = useState7(true);
1097
+ const [error, setError] = useState7(null);
1098
+ const checkAnyPermission = useCallback6(async () => {
851
1099
  if (!userId || permissions.length === 0) {
852
1100
  setHasAny(false);
853
1101
  setIsLoading(false);
@@ -858,7 +1106,7 @@ function useHasAnyPermission(userId, scope, permissions, useCache = true) {
858
1106
  setError(null);
859
1107
  let hasAnyPermission = false;
860
1108
  for (const permission of permissions) {
861
- const result = useCache ? await isPermittedCached({ userId, scope, permission }) : await isPermitted({ userId, scope, permission });
1109
+ const result = useCache ? await isPermittedCached({ userId, scope, permission }) : await isPermitted({ userId, scope, permission }, null, void 0, null);
862
1110
  if (result) {
863
1111
  hasAnyPermission = true;
864
1112
  break;
@@ -872,93 +1120,203 @@ function useHasAnyPermission(userId, scope, permissions, useCache = true) {
872
1120
  setIsLoading(false);
873
1121
  }
874
1122
  }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);
875
- useEffect2(() => {
1123
+ useEffect7(() => {
876
1124
  checkAnyPermission();
877
1125
  }, [checkAnyPermission]);
878
- return useMemo2(() => ({
1126
+ return useMemo7(() => ({
879
1127
  hasAny,
880
1128
  isLoading,
881
1129
  error,
882
1130
  refetch: checkAnyPermission
883
1131
  }), [hasAny, isLoading, error, checkAnyPermission]);
884
1132
  }
885
- function useHasAllPermissions(userId, scope, permissions, useCache = true) {
886
- const [hasAll, setHasAll] = useState2(false);
887
- const [isLoading, setIsLoading] = useState2(true);
888
- const [error, setError] = useState2(null);
889
- const checkAllPermissions = useCallback2(async () => {
1133
+
1134
+ // src/rbac/hooks/permissions/useMultiplePermissions.ts
1135
+ import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo8, useState as useState8 } from "react";
1136
+ function useMultiplePermissions(userId, scope, permissions, useCache = true) {
1137
+ const [results, setResults] = useState8({});
1138
+ const [isLoading, setIsLoading] = useState8(true);
1139
+ const [error, setError] = useState8(null);
1140
+ const checkPermissions = useCallback7(async () => {
890
1141
  if (!userId || permissions.length === 0) {
891
- setHasAll(false);
1142
+ setResults({});
892
1143
  setIsLoading(false);
893
1144
  return;
894
1145
  }
895
1146
  try {
896
1147
  setIsLoading(true);
897
1148
  setError(null);
898
- let hasAllPermissions = true;
1149
+ const permissionResults = {};
899
1150
  for (const permission of permissions) {
900
- const result = useCache ? await isPermittedCached({ userId, scope, permission }) : await isPermitted({ userId, scope, permission });
901
- if (!result) {
902
- hasAllPermissions = false;
903
- break;
904
- }
1151
+ const result = useCache ? await isPermittedCached({ userId, scope, permission }) : await isPermitted({ userId, scope, permission }, null, void 0, null);
1152
+ permissionResults[permission] = result;
905
1153
  }
906
- setHasAll(hasAllPermissions);
1154
+ setResults(permissionResults);
907
1155
  } catch (err) {
908
1156
  setError(err instanceof Error ? err : new Error("Failed to check permissions"));
909
- setHasAll(false);
1157
+ setResults({});
910
1158
  } finally {
911
1159
  setIsLoading(false);
912
1160
  }
913
1161
  }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);
914
- useEffect2(() => {
915
- checkAllPermissions();
916
- }, [checkAllPermissions]);
917
- return useMemo2(() => ({
918
- hasAll,
1162
+ useEffect8(() => {
1163
+ checkPermissions();
1164
+ }, [checkPermissions]);
1165
+ return useMemo8(() => ({
1166
+ results,
919
1167
  isLoading,
920
1168
  error,
921
- refetch: checkAllPermissions
922
- }), [hasAll, isLoading, error, checkAllPermissions]);
1169
+ refetch: checkPermissions
1170
+ }), [results, isLoading, error, checkPermissions]);
923
1171
  }
924
- function useCachedPermissions(userId, scope) {
925
- const [permissions, setPermissions] = useState2({});
926
- const [isLoading, setIsLoading] = useState2(true);
927
- const [error, setError] = useState2(null);
928
- const fetchCachedPermissions = useCallback2(async () => {
1172
+
1173
+ // src/rbac/hooks/permissions/usePermissions.ts
1174
+ import { useCallback as useCallback8, useEffect as useEffect9, useMemo as useMemo9, useRef as useRef3, useState as useState9 } from "react";
1175
+ function usePermissions(userId, organisationId, eventId, appId) {
1176
+ const [permissions, setPermissions] = useState9({});
1177
+ const [isLoading, setIsLoading] = useState9(true);
1178
+ const [error, setError] = useState9(null);
1179
+ const [fetchTrigger, setFetchTrigger] = useState9(0);
1180
+ const isFetchingRef = useRef3(false);
1181
+ const logger2 = getRBACLogger();
1182
+ const prevValuesRef = useRef3({ userId, organisationId, eventId, appId });
1183
+ const orgId = organisationId || "";
1184
+ useEffect9(() => {
1185
+ if (!userId) {
1186
+ return;
1187
+ }
1188
+ if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
1189
+ const timeoutId = setTimeout(() => {
1190
+ setError(new Error("Organisation context is required for permission checks"));
1191
+ setIsLoading(false);
1192
+ }, 3e3);
1193
+ return () => clearTimeout(timeoutId);
1194
+ }
1195
+ if (error?.message === "Organisation context is required for permission checks") {
1196
+ setError(null);
1197
+ }
1198
+ }, [userId, organisationId, error, orgId]);
1199
+ useEffect9(() => {
1200
+ const paramsChanged = prevValuesRef.current.userId !== userId || prevValuesRef.current.organisationId !== organisationId || prevValuesRef.current.eventId !== eventId || prevValuesRef.current.appId !== appId;
1201
+ if (paramsChanged) {
1202
+ if (prevValuesRef.current.appId !== appId) {
1203
+ }
1204
+ prevValuesRef.current = { userId, organisationId, eventId, appId };
1205
+ setFetchTrigger((prev) => prev + 1);
1206
+ }
1207
+ }, [userId, organisationId, eventId, appId, logger2]);
1208
+ useEffect9(() => {
1209
+ const fetchPermissions = async () => {
1210
+ if (isFetchingRef.current) {
1211
+ return;
1212
+ }
1213
+ if (!userId) {
1214
+ setPermissions({});
1215
+ setIsLoading(false);
1216
+ return;
1217
+ }
1218
+ if (!userId) {
1219
+ setPermissions({});
1220
+ setIsLoading(false);
1221
+ return;
1222
+ }
1223
+ if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
1224
+ setIsLoading(true);
1225
+ setError(null);
1226
+ return;
1227
+ }
1228
+ try {
1229
+ isFetchingRef.current = true;
1230
+ setIsLoading(true);
1231
+ setError(null);
1232
+ const scope = {
1233
+ organisationId: orgId,
1234
+ eventId,
1235
+ appId
1236
+ };
1237
+ const permissionMap = await getPermissionMap({ userId, scope });
1238
+ const permissionCount = Object.keys(permissionMap).length;
1239
+ if (permissionCount === 0 && Object.keys(permissions).length > 0) {
1240
+ logger2.warn("[usePermissions] Permissions fetched but returned empty map", {
1241
+ scope: { organisationId: orgId, eventId, appId }
1242
+ });
1243
+ }
1244
+ setPermissions(permissionMap);
1245
+ } catch (err) {
1246
+ logger2.error("[usePermissions] Failed to fetch permissions:", err);
1247
+ setError(err instanceof Error ? err : new Error("Failed to fetch permissions"));
1248
+ } finally {
1249
+ setIsLoading(false);
1250
+ isFetchingRef.current = false;
1251
+ }
1252
+ };
1253
+ fetchPermissions();
1254
+ }, [fetchTrigger, userId, organisationId, eventId, appId]);
1255
+ const hasPermission = useCallback8((permission) => {
1256
+ if (permissions["*"]) {
1257
+ return true;
1258
+ }
1259
+ return permissions[permission] === true;
1260
+ }, [permissions]);
1261
+ const hasAnyPermission = useCallback8((permissionList) => {
1262
+ if (permissions["*"]) {
1263
+ return true;
1264
+ }
1265
+ return permissionList.some((p) => permissions[p] === true);
1266
+ }, [permissions]);
1267
+ const hasAllPermissions = useCallback8((permissionList) => {
1268
+ if (permissions["*"]) {
1269
+ return true;
1270
+ }
1271
+ return permissionList.every((p) => permissions[p] === true);
1272
+ }, [permissions]);
1273
+ const refetch = useCallback8(async () => {
1274
+ if (isFetchingRef.current) {
1275
+ return;
1276
+ }
929
1277
  if (!userId) {
930
1278
  setPermissions({});
931
1279
  setIsLoading(false);
932
1280
  return;
933
1281
  }
1282
+ if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
1283
+ setIsLoading(true);
1284
+ setError(null);
1285
+ return;
1286
+ }
934
1287
  try {
1288
+ isFetchingRef.current = true;
935
1289
  setIsLoading(true);
936
1290
  setError(null);
1291
+ const scope = {
1292
+ organisationId: orgId,
1293
+ eventId,
1294
+ appId
1295
+ };
937
1296
  const permissionMap = await getPermissionMap({ userId, scope });
938
1297
  setPermissions(permissionMap);
939
1298
  } catch (err) {
940
- setError(err instanceof Error ? err : new Error("Failed to fetch cached permissions"));
1299
+ const logger3 = getRBACLogger();
1300
+ logger3.error("Failed to refetch permissions:", err);
1301
+ setError(err instanceof Error ? err : new Error("Failed to fetch permissions"));
941
1302
  } finally {
942
1303
  setIsLoading(false);
1304
+ isFetchingRef.current = false;
943
1305
  }
944
- }, [userId, scope.organisationId, scope.eventId, scope.appId]);
945
- const invalidateCache = useCallback2(() => {
946
- fetchCachedPermissions();
947
- }, [fetchCachedPermissions]);
948
- useEffect2(() => {
949
- fetchCachedPermissions();
950
- }, [fetchCachedPermissions]);
951
- return useMemo2(() => ({
1306
+ }, [userId, organisationId, eventId, appId]);
1307
+ return useMemo9(() => ({
952
1308
  permissions,
953
1309
  isLoading,
954
1310
  error,
955
- invalidateCache,
956
- refetch: fetchCachedPermissions
957
- }), [permissions, isLoading, error, invalidateCache, fetchCachedPermissions]);
1311
+ hasPermission,
1312
+ hasAnyPermission,
1313
+ hasAllPermissions,
1314
+ refetch
1315
+ }), [permissions, isLoading, error, hasPermission, hasAnyPermission, hasAllPermissions, refetch]);
958
1316
  }
959
1317
 
960
1318
  // src/rbac/hooks/useResourcePermissions.ts
961
- import { useMemo as useMemo3 } from "react";
1319
+ import { useMemo as useMemo10 } from "react";
962
1320
  function useResourcePermissions(resource, options = {}) {
963
1321
  const { enableRead = false, requireScope = true } = options;
964
1322
  const { user, supabase } = useUnifiedAuth();
@@ -986,8 +1344,12 @@ function useResourcePermissions(resource, options = {}) {
986
1344
  `create:${resource}`,
987
1345
  pageId,
988
1346
  // Pass resource name as pageId when appId is available to enable page permission checks
989
- true
1347
+ true,
990
1348
  // useCache
1349
+ null,
1350
+ // precomputedSuperAdmin - not checked yet
1351
+ void 0
1352
+ // appName
991
1353
  );
992
1354
  const { can: canUpdateResult, isLoading: updateLoading, error: updateError } = useCan(
993
1355
  user?.id || "",
@@ -995,8 +1357,12 @@ function useResourcePermissions(resource, options = {}) {
995
1357
  `update:${resource}`,
996
1358
  pageId,
997
1359
  // Pass resource name as pageId when appId is available to enable page permission checks
998
- true
1360
+ true,
999
1361
  // useCache
1362
+ null,
1363
+ // precomputedSuperAdmin - not checked yet
1364
+ void 0
1365
+ // appName
1000
1366
  );
1001
1367
  const { can: canDeleteResult, isLoading: deleteLoading, error: deleteError } = useCan(
1002
1368
  user?.id || "",
@@ -1004,8 +1370,12 @@ function useResourcePermissions(resource, options = {}) {
1004
1370
  `delete:${resource}`,
1005
1371
  pageId,
1006
1372
  // Pass resource name as pageId when appId is available to enable page permission checks
1007
- true
1373
+ true,
1008
1374
  // useCache
1375
+ null,
1376
+ // precomputedSuperAdmin - not checked yet
1377
+ void 0
1378
+ // appName
1009
1379
  );
1010
1380
  const { can: canReadResult, isLoading: readLoading, error: readError } = useCan(
1011
1381
  user?.id || "",
@@ -1013,13 +1383,17 @@ function useResourcePermissions(resource, options = {}) {
1013
1383
  `read:${resource}`,
1014
1384
  pageId,
1015
1385
  // Pass resource name as pageId when appId is available to enable page permission checks
1016
- true
1386
+ true,
1017
1387
  // useCache
1388
+ null,
1389
+ // precomputedSuperAdmin - not checked yet
1390
+ void 0
1391
+ // appName
1018
1392
  );
1019
- const isLoading = useMemo3(() => {
1393
+ const isLoading = useMemo10(() => {
1020
1394
  return scopeLoading || createLoading || updateLoading || deleteLoading || enableRead && readLoading;
1021
1395
  }, [scopeLoading, createLoading, updateLoading, deleteLoading, readLoading, enableRead]);
1022
- const error = useMemo3(() => {
1396
+ const error = useMemo10(() => {
1023
1397
  if (scopeError) return scopeError;
1024
1398
  if (createError) return createError;
1025
1399
  if (updateError) return updateError;
@@ -1027,7 +1401,7 @@ function useResourcePermissions(resource, options = {}) {
1027
1401
  if (enableRead && readError) return readError;
1028
1402
  return null;
1029
1403
  }, [scopeError, createError, updateError, deleteError, readError, enableRead]);
1030
- return useMemo3(() => ({
1404
+ return useMemo10(() => ({
1031
1405
  canCreate: (res) => {
1032
1406
  if (res !== resource) {
1033
1407
  return false;
@@ -1072,15 +1446,15 @@ function useResourcePermissions(resource, options = {}) {
1072
1446
  }
1073
1447
 
1074
1448
  // src/rbac/hooks/useRoleManagement.ts
1075
- import { useState as useState3, useCallback as useCallback3 } from "react";
1449
+ import { useState as useState10, useCallback as useCallback9 } from "react";
1076
1450
  function useRoleManagement() {
1077
1451
  const { user, supabase } = useUnifiedAuth();
1078
- const [isLoading, setIsLoading] = useState3(false);
1079
- const [error, setError] = useState3(null);
1452
+ const [isLoading, setIsLoading] = useState10(false);
1453
+ const [error, setError] = useState10(null);
1080
1454
  if (!supabase) {
1081
1455
  throw new Error("useRoleManagement requires a Supabase client. Ensure UnifiedAuthProvider is configured.");
1082
1456
  }
1083
- const revokeEventAppRole = useCallback3(async (params) => {
1457
+ const revokeEventAppRole = useCallback9(async (params) => {
1084
1458
  setIsLoading(true);
1085
1459
  setError(null);
1086
1460
  try {
@@ -1111,7 +1485,7 @@ function useRoleManagement() {
1111
1485
  setIsLoading(false);
1112
1486
  }
1113
1487
  }, [user?.id]);
1114
- const grantEventAppRole = useCallback3(async (params) => {
1488
+ const grantEventAppRole = useCallback9(async (params) => {
1115
1489
  setIsLoading(true);
1116
1490
  setError(null);
1117
1491
  try {
@@ -1150,7 +1524,7 @@ function useRoleManagement() {
1150
1524
  setIsLoading(false);
1151
1525
  }
1152
1526
  }, [user?.id]);
1153
- const revokeRoleById = useCallback3(async (roleId) => {
1527
+ const revokeRoleById = useCallback9(async (roleId) => {
1154
1528
  setIsLoading(true);
1155
1529
  setError(null);
1156
1530
  try {
@@ -1186,7 +1560,7 @@ function useRoleManagement() {
1186
1560
  setIsLoading(false);
1187
1561
  }
1188
1562
  }, [user?.id, supabase]);
1189
- const grantGlobalRole = useCallback3(async (params) => {
1563
+ const grantGlobalRole = useCallback9(async (params) => {
1190
1564
  setIsLoading(true);
1191
1565
  setError(null);
1192
1566
  try {
@@ -1225,7 +1599,7 @@ function useRoleManagement() {
1225
1599
  setIsLoading(false);
1226
1600
  }
1227
1601
  }, [user?.id, supabase]);
1228
- const revokeGlobalRole = useCallback3(async (params) => {
1602
+ const revokeGlobalRole = useCallback9(async (params) => {
1229
1603
  setIsLoading(true);
1230
1604
  setError(null);
1231
1605
  try {
@@ -1257,7 +1631,7 @@ function useRoleManagement() {
1257
1631
  setIsLoading(false);
1258
1632
  }
1259
1633
  }, [user?.id, supabase]);
1260
- const grantOrganisationRole = useCallback3(async (params) => {
1634
+ const grantOrganisationRole = useCallback9(async (params) => {
1261
1635
  setIsLoading(true);
1262
1636
  setError(null);
1263
1637
  try {
@@ -1296,7 +1670,7 @@ function useRoleManagement() {
1296
1670
  setIsLoading(false);
1297
1671
  }
1298
1672
  }, [user?.id, supabase]);
1299
- const revokeOrganisationRole = useCallback3(async (params) => {
1673
+ const revokeOrganisationRole = useCallback9(async (params) => {
1300
1674
  setIsLoading(true);
1301
1675
  setError(null);
1302
1676
  try {
@@ -1346,11 +1720,11 @@ function useRoleManagement() {
1346
1720
  }
1347
1721
 
1348
1722
  // src/rbac/hooks/useSecureSupabase.ts
1349
- import { useMemo as useMemo4, useRef as useRef2 } from "react";
1723
+ import { useMemo as useMemo11, useRef as useRef4 } from "react";
1350
1724
  var secureClientCache = /* @__PURE__ */ new Map();
1351
1725
  var MAX_CACHE_SIZE = 5;
1352
1726
  function getCacheKey(organisationId, eventId, appId, isSuperAdmin) {
1353
- return `${organisationId}-${eventId || "no-event"}-${appId || "no-app"}-${isSuperAdmin ? "super" : "regular"}`;
1727
+ return `${organisationId || "no-org"}-${eventId || "no-event"}-${appId || "no-app"}-${isSuperAdmin ? "super" : "regular"}`;
1354
1728
  }
1355
1729
  function getSupabaseConfig() {
1356
1730
  const getEnvVar = (key) => {
@@ -1382,19 +1756,20 @@ function useSecureSupabase(baseClient) {
1382
1756
  selectedOrganisationId: selectedOrganisation?.id || null,
1383
1757
  selectedEventId: selectedEvent?.event_id || null
1384
1758
  });
1385
- const prevContextRef = useRef2({
1759
+ const prevContextRef = useRef4({
1386
1760
  organisationId: void 0,
1387
1761
  eventId: void 0,
1388
1762
  appId: void 0
1389
1763
  });
1390
- return useMemo4(() => {
1764
+ return useMemo11(() => {
1391
1765
  if (eventLoading) {
1392
1766
  return baseClient || authSupabase || null;
1393
1767
  }
1394
1768
  const organisationId = resolvedScope?.organisationId;
1395
1769
  const eventId = resolvedScope?.eventId || selectedEvent?.event_id;
1396
1770
  const appId = resolvedScope?.appId;
1397
- if (organisationId && user?.id) {
1771
+ const canCreateSecureClient = user?.id && (isSuperAdmin || organisationId);
1772
+ if (canCreateSecureClient) {
1398
1773
  prevContextRef.current = { organisationId, eventId, appId };
1399
1774
  const cacheKey = getCacheKey(organisationId, eventId, appId, isSuperAdmin);
1400
1775
  const cachedClient = secureClientCache.get(cacheKey);
@@ -1409,11 +1784,19 @@ function useSecureSupabase(baseClient) {
1409
1784
  return baseClient || authSupabase || null;
1410
1785
  }
1411
1786
  try {
1412
- const secureClient = createSecureClient(
1787
+ const effectiveOrganisationId = isSuperAdmin ? organisationId || null : organisationId;
1788
+ const baseForSecureClient = baseClient || authSupabase || null;
1789
+ const secureClient = baseForSecureClient ? fromSupabaseClient(
1790
+ baseForSecureClient,
1791
+ effectiveOrganisationId ?? null,
1792
+ eventId,
1793
+ appId,
1794
+ isSuperAdmin
1795
+ ) : createSecureClient(
1413
1796
  config.url,
1414
1797
  config.key,
1415
- organisationId,
1416
- // organisationId is string, UUID is string alias
1798
+ effectiveOrganisationId,
1799
+ // organisationId is string | null, UUID is string alias
1417
1800
  eventId,
1418
1801
  appId,
1419
1802
  // appId is string | undefined, UUID is string alias
@@ -1451,17 +1834,18 @@ export {
1451
1834
  SecureSupabaseClient,
1452
1835
  createSecureClient,
1453
1836
  fromSupabaseClient,
1837
+ useResolvedScope,
1454
1838
  useRBAC,
1839
+ useAccessLevel,
1840
+ useCachedPermissions,
1455
1841
  scopeEqual,
1456
- usePermissions,
1457
1842
  useCan,
1458
- useAccessLevel,
1459
- useMultiplePermissions,
1460
- useHasAnyPermission,
1461
1843
  useHasAllPermissions,
1462
- useCachedPermissions,
1844
+ useHasAnyPermission,
1845
+ useMultiplePermissions,
1846
+ usePermissions,
1463
1847
  useResourcePermissions,
1464
1848
  useRoleManagement,
1465
1849
  useSecureSupabase
1466
1850
  };
1467
- //# sourceMappingURL=chunk-XNXXZ43G.js.map
1851
+ //# sourceMappingURL=chunk-XWQCNGTQ.js.map