@jmruthers/pace-core 0.6.1 → 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 (502) hide show
  1. package/CHANGELOG.md +43 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +18 -91
  3. package/cursor-rules/01-standards-compliance.mdc +16 -47
  4. package/cursor-rules/02-project-structure.mdc +4 -4
  5. package/cursor-rules/03-solid-principles.mdc +45 -164
  6. package/cursor-rules/04-testing-standards.mdc +22 -69
  7. package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
  8. package/cursor-rules/06-code-quality.mdc +42 -125
  9. package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
  10. package/cursor-rules/08-markup-quality.mdc +452 -0
  11. package/cursor-rules/CHANGELOG.md +18 -0
  12. package/cursor-rules/README.md +2 -1
  13. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-TPTKCX4D.js} +10 -9
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +356 -111
  17. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  18. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  19. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  20. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  21. package/dist/chunk-24UVZUZG.js.map +1 -0
  22. package/dist/{chunk-4N5C5XZU.js → chunk-2UOI2FG5.js} +4 -4
  23. package/dist/chunk-2UOI2FG5.js.map +1 -0
  24. package/dist/{chunk-T33XF5ZC.js → chunk-3XC4CPTD.js} +4317 -3963
  25. package/dist/chunk-3XC4CPTD.js.map +1 -0
  26. package/dist/{chunk-4ZC4GX36.js → chunk-6J4GEEJR.js} +172 -45
  27. package/dist/chunk-6J4GEEJR.js.map +1 -0
  28. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  29. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-EHMR7VYL.js} +4 -4
  31. package/dist/chunk-EHMR7VYL.js.map +1 -0
  32. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  33. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  34. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  35. package/dist/chunk-FFQEQTNW.js.map +1 -0
  36. package/dist/chunk-FMUCXFII.js +76 -0
  37. package/dist/chunk-FMUCXFII.js.map +1 -0
  38. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  39. package/dist/chunk-L4OXEN46.js.map +1 -0
  40. package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
  41. package/dist/chunk-M43Y4SSO.js.map +1 -0
  42. package/dist/{chunk-3XTALGJF.js → chunk-MMZ7JXPU.js} +60 -223
  43. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  44. package/dist/{chunk-GLK6VM3F.js → chunk-NECFR5MM.js} +254 -170
  45. package/dist/chunk-NECFR5MM.js.map +1 -0
  46. package/dist/{chunk-JBKQ3SAO.js → chunk-SFZUDBL5.js} +40 -4
  47. package/dist/chunk-SFZUDBL5.js.map +1 -0
  48. package/dist/{chunk-XM25TVIE.js → chunk-XWQCNGTQ.js} +724 -363
  49. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  50. package/dist/components.d.ts +5 -5
  51. package/dist/components.js +14 -11
  52. package/dist/components.js.map +1 -1
  53. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  54. package/dist/hooks.d.ts +55 -122
  55. package/dist/hooks.js +8 -12
  56. package/dist/hooks.js.map +1 -1
  57. package/dist/index.d.ts +60 -13
  58. package/dist/index.js +19 -19
  59. package/dist/index.js.map +1 -1
  60. package/dist/providers.d.ts +21 -3
  61. package/dist/providers.js +2 -2
  62. package/dist/rbac/index.d.ts +145 -114
  63. package/dist/rbac/index.js +8 -11
  64. package/dist/theming/runtime.d.ts +1 -13
  65. package/dist/theming/runtime.js +1 -1
  66. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  67. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  68. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  69. package/dist/types.d.ts +2 -2
  70. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +31 -1
  71. package/dist/utils.d.ts +4 -5
  72. package/dist/utils.js +14 -14
  73. package/dist/utils.js.map +1 -1
  74. package/docs/api/README.md +7 -1
  75. package/docs/api/classes/ColumnFactory.md +8 -8
  76. package/docs/api/classes/InvalidScopeError.md +4 -4
  77. package/docs/api/classes/Logger.md +1 -1
  78. package/docs/api/classes/MissingUserContextError.md +4 -4
  79. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  80. package/docs/api/classes/PermissionDeniedError.md +4 -4
  81. package/docs/api/classes/RBACAuditManager.md +1 -1
  82. package/docs/api/classes/RBACCache.md +1 -1
  83. package/docs/api/classes/RBACEngine.md +1 -1
  84. package/docs/api/classes/RBACError.md +4 -4
  85. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  86. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  87. package/docs/api/classes/StorageUtils.md +1 -1
  88. package/docs/api/enums/FileCategory.md +1 -1
  89. package/docs/api/enums/LogLevel.md +1 -1
  90. package/docs/api/enums/RBACErrorCode.md +1 -1
  91. package/docs/api/enums/RPCFunction.md +1 -1
  92. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  93. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  94. package/docs/api/interfaces/AggregateConfig.md +4 -4
  95. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  96. package/docs/api/interfaces/AvatarProps.md +1 -1
  97. package/docs/api/interfaces/BadgeProps.md +9 -2
  98. package/docs/api/interfaces/ButtonProps.md +7 -4
  99. package/docs/api/interfaces/CalendarProps.md +8 -5
  100. package/docs/api/interfaces/CardProps.md +8 -5
  101. package/docs/api/interfaces/ColorPalette.md +1 -1
  102. package/docs/api/interfaces/ColorShade.md +1 -1
  103. package/docs/api/interfaces/ComplianceResult.md +1 -1
  104. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  105. package/docs/api/interfaces/DataRecord.md +1 -1
  106. package/docs/api/interfaces/DataTableAction.md +24 -21
  107. package/docs/api/interfaces/DataTableColumn.md +31 -31
  108. package/docs/api/interfaces/DataTableProps.md +1 -1
  109. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  110. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  111. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  112. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  113. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  114. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  115. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  116. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  117. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  118. package/docs/api/interfaces/ExportColumn.md +1 -1
  119. package/docs/api/interfaces/ExportOptions.md +8 -8
  120. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  121. package/docs/api/interfaces/FileMetadata.md +1 -1
  122. package/docs/api/interfaces/FileReference.md +1 -1
  123. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  124. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  125. package/docs/api/interfaces/FileUploadProps.md +26 -23
  126. package/docs/api/interfaces/FooterProps.md +10 -8
  127. package/docs/api/interfaces/FormFieldProps.md +10 -10
  128. package/docs/api/interfaces/FormProps.md +1 -1
  129. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  130. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  131. package/docs/api/interfaces/InputProps.md +7 -4
  132. package/docs/api/interfaces/LabelProps.md +1 -1
  133. package/docs/api/interfaces/LoggerConfig.md +1 -1
  134. package/docs/api/interfaces/LoginFormProps.md +14 -11
  135. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  136. package/docs/api/interfaces/NavigationContextType.md +1 -1
  137. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  138. package/docs/api/interfaces/NavigationItem.md +11 -11
  139. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  140. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  141. package/docs/api/interfaces/Organisation.md +1 -1
  142. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  143. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  144. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  145. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  146. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  147. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  148. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  149. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  150. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  151. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  152. package/docs/api/interfaces/PaletteData.md +1 -1
  153. package/docs/api/interfaces/ParsedAddress.md +1 -1
  154. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  155. package/docs/api/interfaces/ProgressProps.md +1 -1
  156. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  157. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  158. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  159. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  160. package/docs/api/interfaces/QuickFix.md +1 -1
  161. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  162. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  163. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  164. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  165. package/docs/api/interfaces/RBACConfig.md +1 -1
  166. package/docs/api/interfaces/RBACContext.md +1 -1
  167. package/docs/api/interfaces/RBACLogger.md +1 -1
  168. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  169. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  170. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  171. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  172. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  173. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  174. package/docs/api/interfaces/RBACResult.md +1 -1
  175. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  176. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  177. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  178. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  181. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  182. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  183. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  184. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  185. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  186. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  187. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  188. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  189. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  190. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  191. package/docs/api/interfaces/RouteConfig.md +1 -1
  192. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  193. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  194. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  195. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  196. package/docs/api/interfaces/SetupIssue.md +1 -1
  197. package/docs/api/interfaces/StorageConfig.md +1 -1
  198. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  199. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  200. package/docs/api/interfaces/StorageListOptions.md +1 -1
  201. package/docs/api/interfaces/StorageListResult.md +1 -1
  202. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  203. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  204. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  205. package/docs/api/interfaces/StyleImport.md +1 -1
  206. package/docs/api/interfaces/SwitchProps.md +1 -1
  207. package/docs/api/interfaces/TabsContentProps.md +1 -1
  208. package/docs/api/interfaces/TabsListProps.md +1 -1
  209. package/docs/api/interfaces/TabsProps.md +1 -1
  210. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  211. package/docs/api/interfaces/TextareaProps.md +1 -1
  212. package/docs/api/interfaces/ToastActionElement.md +4 -1
  213. package/docs/api/interfaces/ToastProps.md +1 -1
  214. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  215. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  216. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  217. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  218. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  219. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  220. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  221. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  222. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  223. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  224. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  225. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  226. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  227. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  228. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  229. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  230. package/docs/api/interfaces/UserEventAccess.md +14 -11
  231. package/docs/api/interfaces/UserMenuProps.md +8 -6
  232. package/docs/api/interfaces/UserProfile.md +1 -1
  233. package/docs/api/modules.md +575 -634
  234. package/docs/architecture/database-schema-requirements.md +161 -0
  235. package/docs/core-concepts/rbac-system.md +3 -3
  236. package/docs/documentation-index.md +2 -4
  237. package/docs/getting-started/cursor-rules.md +2 -1
  238. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  239. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  240. package/docs/migration/README.md +52 -6
  241. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  242. package/docs/migration/database-changes-december-2025.md +3 -3
  243. package/docs/rbac/event-based-apps.md +1 -1
  244. package/docs/rbac/getting-started.md +1 -1
  245. package/docs/rbac/quick-start.md +1 -1
  246. package/docs/standards/README.md +1 -0
  247. package/package.json +2 -1
  248. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  249. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  250. package/scripts/audit/core/checks/bundle.cjs +142 -0
  251. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +714 -687
  252. package/scripts/audit/core/checks/config.cjs +54 -0
  253. package/scripts/audit/core/checks/coverage.cjs +84 -0
  254. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  255. package/scripts/audit/core/checks/documentation.cjs +203 -0
  256. package/scripts/audit/core/checks/environment.cjs +128 -0
  257. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  258. package/scripts/audit/core/checks/forms.cjs +172 -0
  259. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  260. package/scripts/audit/core/checks/hooks.cjs +334 -0
  261. package/scripts/audit/core/checks/imports.cjs +244 -0
  262. package/scripts/audit/core/checks/performance.cjs +325 -0
  263. package/scripts/audit/core/checks/routes.cjs +117 -0
  264. package/scripts/audit/core/checks/state.cjs +130 -0
  265. package/scripts/audit/core/checks/structure.cjs +65 -0
  266. package/scripts/audit/core/checks/style.cjs +584 -0
  267. package/scripts/audit/core/checks/testing.cjs +122 -0
  268. package/scripts/audit/core/checks/typescript.cjs +61 -0
  269. package/scripts/audit/core/scanner.cjs +199 -0
  270. package/scripts/audit/core/utils.cjs +137 -0
  271. package/scripts/audit/index.cjs +223 -0
  272. package/scripts/audit/reporters/console.cjs +151 -0
  273. package/scripts/audit/reporters/json.cjs +54 -0
  274. package/scripts/audit/reporters/markdown.cjs +124 -0
  275. package/scripts/audit-consuming-app.cjs +61 -936
  276. package/scripts/build-docs/build-decision.js +240 -0
  277. package/scripts/build-docs/cache-utils.js +105 -0
  278. package/scripts/build-docs/content-normalization.js +150 -0
  279. package/scripts/build-docs/file-utils.js +105 -0
  280. package/scripts/build-docs/git-utils.js +86 -0
  281. package/scripts/build-docs/hash-utils.js +116 -0
  282. package/scripts/build-docs/typedoc-runner.js +220 -0
  283. package/scripts/build-docs-incremental.js +77 -913
  284. package/scripts/utils/command-runner.js +16 -11
  285. package/scripts/validate-formats.js +61 -56
  286. package/scripts/validate-master.js +74 -69
  287. package/scripts/validate-pre-publish.js +70 -65
  288. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  289. package/src/components/Alert/Alert.test.tsx +12 -18
  290. package/src/components/Alert/Alert.tsx +5 -7
  291. package/src/components/Avatar/Avatar.test.tsx +4 -4
  292. package/src/components/Badge/Badge.tsx +14 -0
  293. package/src/components/Button/Button.tsx +22 -0
  294. package/src/components/Calendar/Calendar.tsx +8 -2
  295. package/src/components/Card/Card.tsx +4 -0
  296. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  297. package/src/components/Checkbox/Checkbox.tsx +2 -2
  298. package/src/components/DataTable/DataTable.tsx +38 -4
  299. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  300. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  301. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  302. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  303. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  304. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  305. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  306. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  307. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  308. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  309. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  310. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  311. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  312. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  313. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  314. package/src/components/DataTable/components/EditFields.tsx +307 -0
  315. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  316. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  317. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  318. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  319. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  320. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  321. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  322. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  323. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  324. package/src/components/DataTable/components/UnifiedTableBody.tsx +61 -849
  325. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  326. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  327. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  328. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  329. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  330. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  331. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  332. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  333. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  334. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  335. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  336. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  337. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  338. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  339. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  340. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  341. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  342. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  343. package/src/components/DataTable/styles.ts +6 -6
  344. package/src/components/DataTable/types.ts +6 -10
  345. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  346. package/src/components/DataTable/utils/debugTools.ts +18 -113
  347. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  348. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  349. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  350. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  351. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  352. package/src/components/Dialog/Dialog.tsx +2 -2
  353. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  354. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  355. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  356. package/src/components/ErrorBoundary/index.ts +27 -2
  357. package/src/components/EventSelector/EventSelector.tsx +3 -0
  358. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  359. package/src/components/FileUpload/FileUpload.tsx +22 -2
  360. package/src/components/Footer/Footer.test.tsx +16 -16
  361. package/src/components/Footer/Footer.tsx +14 -11
  362. package/src/components/Form/Form.tsx +1 -0
  363. package/src/components/Header/Header.tsx +21 -10
  364. package/src/components/Input/Input.test.tsx +2 -2
  365. package/src/components/Input/Input.tsx +8 -4
  366. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  367. package/src/components/LoginForm/LoginForm.tsx +4 -0
  368. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  369. package/src/components/NavigationMenu/types.ts +56 -0
  370. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  371. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  372. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -2
  373. package/src/components/PaceAppLayout/PaceAppLayout.tsx +32 -11
  374. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  375. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  376. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  377. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  378. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  379. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  380. package/src/components/Select/Select.tsx +80 -434
  381. package/src/components/Select/context.ts +23 -0
  382. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  383. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  384. package/src/components/Select/hooks/useSelectState.ts +104 -0
  385. package/src/components/Select/index.ts +9 -1
  386. package/src/components/Select/types.ts +123 -0
  387. package/src/components/Select/utils/text.ts +26 -0
  388. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  389. package/src/components/Switch/Switch.tsx +4 -4
  390. package/src/components/Tabs/Tabs.tsx +1 -1
  391. package/src/components/Toast/Toast.tsx +4 -0
  392. package/src/components/Tooltip/Tooltip.tsx +2 -2
  393. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  394. package/src/components/UserMenu/UserMenu.tsx +21 -18
  395. package/src/components/index.ts +2 -2
  396. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  397. package/src/hooks/index.ts +1 -2
  398. package/src/hooks/public/usePublicEvent.ts +4 -0
  399. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  400. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  401. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  402. package/src/hooks/services/useAuth.ts +32 -0
  403. package/src/hooks/services/useCurrentEvent.ts +6 -0
  404. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  405. package/src/hooks/useDebounce.ts +9 -0
  406. package/src/hooks/useEventTheme.ts +6 -0
  407. package/src/hooks/useFileDisplay.ts +4 -0
  408. package/src/hooks/useFileReference.ts +25 -7
  409. package/src/hooks/useFileUrl.ts +11 -1
  410. package/src/hooks/useFocusManagement.ts +14 -0
  411. package/src/hooks/useFocusTrap.ts +3 -0
  412. package/src/hooks/useInactivityTracker.ts +3 -0
  413. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  414. package/src/hooks/useOrganisationPermissions.ts +4 -0
  415. package/src/hooks/useOrganisationSecurity.ts +4 -0
  416. package/src/hooks/usePerformanceMonitor.ts +4 -0
  417. package/src/hooks/usePermissionCache.ts +7 -0
  418. package/src/hooks/useQueryCache.ts +12 -1
  419. package/src/hooks/useSessionRestoration.ts +4 -0
  420. package/src/hooks/useStorage.ts +4 -0
  421. package/src/hooks/useToast.ts +1 -1
  422. package/src/index.ts +2 -1
  423. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  424. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  425. package/src/providers/services/EventServiceProvider.tsx +18 -0
  426. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  427. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  428. package/src/providers/services/UnifiedAuthProvider.tsx +36 -0
  429. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  430. package/src/rbac/README.md +1 -1
  431. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +2 -2
  432. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  433. package/src/rbac/adapters.tsx +14 -5
  434. package/src/rbac/api.ts +100 -67
  435. package/src/rbac/components/NavigationProvider.tsx +4 -1
  436. package/src/rbac/components/PagePermissionGuard.tsx +157 -17
  437. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  438. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  439. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  440. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  441. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  442. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  443. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  444. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  445. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  446. package/src/rbac/engine.ts +38 -14
  447. package/src/rbac/hooks/permissions/index.ts +7 -0
  448. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  449. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  450. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  451. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  452. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  453. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  454. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  455. package/src/rbac/hooks/useCan.test.ts +71 -64
  456. package/src/rbac/hooks/usePermissions.ts +14 -995
  457. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  458. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  459. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  460. package/src/rbac/permissions.ts +0 -30
  461. package/src/rbac/secureClient.ts +200 -61
  462. package/src/rbac/types.ts +8 -0
  463. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  464. package/src/theming/parseEventColours.ts +5 -19
  465. package/src/types/vitest-globals.d.ts +51 -26
  466. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  467. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  468. package/src/utils/__tests__/index.unit.test.ts +2 -2
  469. package/src/utils/audit/audit.ts +0 -3
  470. package/src/utils/core/cn.ts +1 -1
  471. package/src/utils/file-reference/index.ts +53 -1
  472. package/src/utils/formatting/formatting.ts +8 -18
  473. package/src/utils/index.ts +0 -1
  474. package/dist/chunk-3QRJFVBR.js.map +0 -1
  475. package/dist/chunk-3XTALGJF.js.map +0 -1
  476. package/dist/chunk-4N5C5XZU.js.map +0 -1
  477. package/dist/chunk-4ZC4GX36.js.map +0 -1
  478. package/dist/chunk-BYFSK72L.js.map +0 -1
  479. package/dist/chunk-EXUD6RNJ.js +0 -451
  480. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  481. package/dist/chunk-GLK6VM3F.js.map +0 -1
  482. package/dist/chunk-I7PSE6JW.js.map +0 -1
  483. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  484. package/dist/chunk-KNC55RTG.js.map +0 -1
  485. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  486. package/dist/chunk-R77UEZ4E.js.map +0 -1
  487. package/dist/chunk-SQGMNID3.js.map +0 -1
  488. package/dist/chunk-T33XF5ZC.js.map +0 -1
  489. package/dist/chunk-XM25TVIE.js.map +0 -1
  490. package/docs/api/classes/ErrorBoundary.md +0 -144
  491. package/docs/migration/quick-migration-guide.md +0 -356
  492. package/docs/migration/service-architecture.md +0 -281
  493. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  494. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  495. package/src/hooks/useSecureDataAccess.ts +0 -681
  496. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  497. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  498. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  499. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  500. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  501. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  502. /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
@@ -1,13 +1,12 @@
1
1
  import {
2
2
  useAppConfig,
3
3
  useEvents,
4
- useOrganisationSecurity,
5
- useResolvedScope
6
- } from "./chunk-3XTALGJF.js";
4
+ useOrganisationSecurity
5
+ } from "./chunk-MMZ7JXPU.js";
7
6
  import {
8
7
  useOrganisations,
9
8
  useUnifiedAuth
10
- } from "./chunk-BYFSK72L.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
  */
@@ -189,6 +216,13 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
189
216
  if (!this.organisationId) {
190
217
  return query;
191
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
+ }
192
226
  if (tableName === "rbac_user_profiles") {
193
227
  if (this.isSuperAdmin) {
194
228
  return query;
@@ -202,12 +236,57 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
202
236
  }
203
237
  /**
204
238
  * Validate that required context is present
239
+ * Super-admins can operate without organisation context
205
240
  */
206
241
  validateContext() {
242
+ if (this.isSuperAdmin) {
243
+ return;
244
+ }
207
245
  if (!this.organisationId) {
208
246
  throw new OrganisationContextRequiredError();
209
247
  }
210
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
+ }
211
290
  /**
212
291
  * Get the current organisation ID
213
292
  */
@@ -233,7 +312,7 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
233
312
  return new _SecureSupabaseClient(
234
313
  this.supabaseUrl,
235
314
  this.supabaseKey,
236
- updates.organisationId || this.organisationId,
315
+ updates.organisationId !== void 0 ? updates.organisationId : this.organisationId,
237
316
  updates.eventId !== void 0 ? updates.eventId : this.eventId,
238
317
  updates.appId !== void 0 ? updates.appId : this.appId,
239
318
  updates.isSuperAdmin !== void 0 ? updates.isSuperAdmin : this.isSuperAdmin
@@ -254,15 +333,216 @@ var SecureSupabaseClient = class _SecureSupabaseClient {
254
333
  });
255
334
  }
256
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;
257
350
  function createSecureClient(supabaseUrl, supabaseKey, organisationId, eventId, appId, isSuperAdmin = false) {
258
351
  return new SecureSupabaseClient(supabaseUrl, supabaseKey, organisationId, eventId, appId, isSuperAdmin);
259
352
  }
260
- function fromSupabaseClient(client, organisationId, eventId, appId) {
261
- 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
+ };
262
542
  }
263
543
 
264
544
  // src/rbac/hooks/useRBAC.ts
265
- import { useState, useEffect, useCallback, useMemo } from "react";
545
+ import { useState as useState2, useEffect as useEffect2, useCallback, useMemo as useMemo2 } from "react";
266
546
  function mapAccessLevelToEventRole(level) {
267
547
  switch (level) {
268
548
  case "viewer":
@@ -293,13 +573,13 @@ function useRBAC(pageId) {
293
573
  selectedEvent,
294
574
  eventLoading
295
575
  } = useUnifiedAuth();
296
- const [globalRole, setGlobalRole] = useState(null);
297
- const [organisationRole, setOrganisationRole] = useState(null);
298
- const [eventAppRole, setEventAppRole] = useState(null);
299
- const [permissionMap, setPermissionMap] = useState({});
300
- const [currentScope, setCurrentScope] = useState(null);
301
- const [isLoading, setIsLoading] = useState(false);
302
- 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);
303
583
  const resetState = useCallback(() => {
304
584
  setGlobalRole(null);
305
585
  setOrganisationRole(null);
@@ -339,7 +619,7 @@ function useRBAC(pageId) {
339
619
  if (!resolved) {
340
620
  if (appName === "PORTAL" || appName === "ADMIN") {
341
621
  try {
342
- const { getAppConfigByName } = await import("./api-N774RPUA.js");
622
+ const { getAppConfigByName } = await import("./api-MVVQZLJI.js");
343
623
  await getAppConfigByName(appName);
344
624
  } catch (err) {
345
625
  }
@@ -424,12 +704,12 @@ function useRBAC(pageId) {
424
704
  },
425
705
  [globalRole, organisationRole, permissionMap]
426
706
  );
427
- const isSuperAdmin = useMemo(() => globalRole === "super_admin" || permissionMap["*"] === true, [globalRole, permissionMap]);
428
- const isOrgAdmin = useMemo(() => organisationRole === "org_admin" || isSuperAdmin, [organisationRole, isSuperAdmin]);
429
- const isEventAdmin = useMemo(() => eventAppRole === "event_admin" || isSuperAdmin, [eventAppRole, isSuperAdmin]);
430
- const canManageOrganisation = useMemo(() => isSuperAdmin || organisationRole === "org_admin", [isSuperAdmin, organisationRole]);
431
- const canManageEvent = useMemo(() => isSuperAdmin || eventAppRole === "event_admin", [isSuperAdmin, eventAppRole]);
432
- 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(() => {
433
713
  loadRBACContext();
434
714
  }, [loadRBACContext, appName, appConfig, eventLoading, selectedEvent?.event_id, user, session, selectedOrganisation?.id, orgContextReady, orgLoading]);
435
715
  return {
@@ -448,197 +728,176 @@ function useRBAC(pageId) {
448
728
  };
449
729
  }
450
730
 
451
- // src/rbac/hooks/usePermissions.ts
452
- import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useMemo as useMemo2, useRef } from "react";
453
-
454
- // src/rbac/utils/deep-equal.ts
455
- function scopeEqual(a, b) {
456
- if (a === b) {
457
- return true;
458
- }
459
- if (a == null || b == null) {
460
- 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 {
461
742
  }
462
- return a.organisationId === b.organisationId && a.eventId === b.eventId && a.appId === b.appId;
463
- }
464
-
465
- // src/rbac/hooks/usePermissions.ts
466
- function usePermissions(userId, organisationId, eventId, appId) {
467
- const [permissions, setPermissions] = useState2({});
468
- const [isLoading, setIsLoading] = useState2(true);
469
- const [error, setError] = useState2(null);
470
- const [fetchTrigger, setFetchTrigger] = useState2(0);
471
- const isFetchingRef = useRef(false);
472
- const logger2 = getRBACLogger();
473
- const prevValuesRef = useRef({ userId, organisationId, eventId, appId });
474
- const orgId = organisationId || "";
475
- useEffect2(() => {
743
+ const fetchAccessLevel = useCallback2(async () => {
476
744
  if (!userId) {
745
+ setAccessLevel("viewer");
746
+ setIsLoading(false);
477
747
  return;
478
748
  }
479
- if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
480
- const timeoutId = setTimeout(() => {
481
- setError(new Error("Organisation context is required for permission checks"));
482
- setIsLoading(false);
483
- }, 3e3);
484
- return () => clearTimeout(timeoutId);
485
- }
486
- if (error?.message === "Organisation context is required for permission checks") {
749
+ try {
750
+ setIsLoading(true);
487
751
  setError(null);
488
- }
489
- }, [userId, organisationId, error, orgId]);
490
- useEffect2(() => {
491
- const paramsChanged = prevValuesRef.current.userId !== userId || prevValuesRef.current.organisationId !== organisationId || prevValuesRef.current.eventId !== eventId || prevValuesRef.current.appId !== appId;
492
- if (paramsChanged) {
493
- if (prevValuesRef.current.appId !== appId) {
494
- }
495
- prevValuesRef.current = { userId, organisationId, eventId, appId };
496
- setFetchTrigger((prev) => prev + 1);
497
- }
498
- }, [userId, organisationId, eventId, appId, logger2]);
499
- useEffect2(() => {
500
- const fetchPermissions = async () => {
501
- if (isFetchingRef.current) {
502
- return;
503
- }
504
- if (!userId) {
505
- setPermissions({});
752
+ const { isSuperAdmin: checkSuperAdmin } = await import("./api-MVVQZLJI.js");
753
+ const isSuperAdminUser = await checkSuperAdmin(userId);
754
+ if (isSuperAdminUser) {
755
+ setAccessLevel("super");
506
756
  setIsLoading(false);
507
757
  return;
508
758
  }
509
- if (!userId) {
510
- setPermissions({});
759
+ if (appName !== "PORTAL" && appName !== "ADMIN" && !scope.organisationId && !scope.eventId) {
760
+ const orgError = new OrganisationContextRequiredError();
761
+ setError(orgError);
762
+ setAccessLevel("viewer");
511
763
  setIsLoading(false);
512
764
  return;
513
765
  }
514
- if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
515
- setIsLoading(true);
516
- setError(null);
517
- return;
518
- }
519
- try {
520
- isFetchingRef.current = true;
521
- setIsLoading(true);
522
- setError(null);
523
- const scope = {
524
- organisationId: orgId,
525
- eventId,
526
- appId
527
- };
528
- const permissionMap = await getPermissionMap({ userId, scope });
529
- const permissionCount = Object.keys(permissionMap).length;
530
- if (permissionCount === 0 && Object.keys(permissions).length > 0) {
531
- logger2.warn("[usePermissions] Permissions fetched but returned empty map", {
532
- scope: { organisationId: orgId, eventId, appId }
533
- });
534
- }
535
- setPermissions(permissionMap);
536
- } catch (err) {
537
- logger2.error("[usePermissions] Failed to fetch permissions:", err);
538
- setError(err instanceof Error ? err : new Error("Failed to fetch permissions"));
539
- } finally {
540
- setIsLoading(false);
541
- isFetchingRef.current = false;
542
- }
543
- };
544
- fetchPermissions();
545
- }, [fetchTrigger, userId, organisationId, eventId, appId]);
546
- const hasPermission = useCallback2((permission) => {
547
- if (permissions["*"]) {
548
- return true;
549
- }
550
- return permissions[permission] === true;
551
- }, [permissions]);
552
- const hasAnyPermission = useCallback2((permissionList) => {
553
- if (permissions["*"]) {
554
- return true;
555
- }
556
- return permissionList.some((p) => permissions[p] === true);
557
- }, [permissions]);
558
- const hasAllPermissions = useCallback2((permissionList) => {
559
- if (permissions["*"]) {
560
- return true;
561
- }
562
- return permissionList.every((p) => permissions[p] === true);
563
- }, [permissions]);
564
- const refetch = useCallback2(async () => {
565
- if (isFetchingRef.current) {
566
- 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);
567
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 () => {
568
794
  if (!userId) {
569
795
  setPermissions({});
570
796
  setIsLoading(false);
571
797
  return;
572
798
  }
573
- if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
574
- setIsLoading(true);
575
- setError(null);
576
- return;
577
- }
578
799
  try {
579
- isFetchingRef.current = true;
580
800
  setIsLoading(true);
581
801
  setError(null);
582
- const scope = {
583
- organisationId: orgId,
584
- eventId,
585
- appId
586
- };
587
802
  const permissionMap = await getPermissionMap({ userId, scope });
588
803
  setPermissions(permissionMap);
589
804
  } catch (err) {
590
- const logger3 = getRBACLogger();
591
- logger3.error("Failed to refetch permissions:", err);
592
- setError(err instanceof Error ? err : new Error("Failed to fetch permissions"));
805
+ setError(err instanceof Error ? err : new Error("Failed to fetch cached permissions"));
593
806
  } finally {
594
807
  setIsLoading(false);
595
- isFetchingRef.current = false;
596
808
  }
597
- }, [userId, organisationId, eventId, appId]);
598
- 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(() => ({
599
817
  permissions,
600
818
  isLoading,
601
819
  error,
602
- hasPermission,
603
- hasAnyPermission,
604
- hasAllPermissions,
605
- refetch
606
- }), [permissions, isLoading, error, hasPermission, hasAnyPermission, hasAllPermissions, refetch]);
820
+ invalidateCache,
821
+ refetch: fetchCachedPermissions
822
+ }), [permissions, isLoading, error, invalidateCache, fetchCachedPermissions]);
607
823
  }
608
- function useCan(userId, scope, permission, pageId, useCache = true, appName) {
609
- const [can, setCan] = useState2(false);
610
- const [isLoading, setIsLoading] = useState2(true);
611
- const [error, setError] = useState2(null);
612
- 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);
613
845
  const isValidScope = scope && typeof scope === "object";
614
846
  const organisationId = isValidScope ? scope.organisationId : void 0;
615
847
  const eventId = isValidScope ? scope.eventId : void 0;
616
848
  const appId = isValidScope ? scope.appId : void 0;
617
- useEffect2(() => {
618
- if (!userId) {
619
- setIsSuperAdmin(false);
620
- return;
621
- }
622
- let cancelled = false;
623
- const checkSuperAdmin = async () => {
624
- try {
625
- const { isSuperAdmin: checkSuperAdmin2 } = await import("./api-N774RPUA.js");
626
- const isSuper = await checkSuperAdmin2(userId);
627
- if (!cancelled) {
628
- setIsSuperAdmin(isSuper);
629
- }
630
- } catch (err) {
631
- if (!cancelled) {
632
- setIsSuperAdmin(false);
633
- }
849
+ useEffect5(() => {
850
+ if (precomputedSuperAdmin === null) {
851
+ if (!userId) {
852
+ setIsSuperAdmin(false);
853
+ return;
634
854
  }
635
- };
636
- checkSuperAdmin();
637
- return () => {
638
- cancelled = true;
639
- };
640
- }, [userId]);
641
- 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(() => {
642
901
  const isPagePermission = permission.includes(":page.") || !!pageId;
643
902
  const requiresOrgId = !isPagePermission;
644
903
  if (isSuperAdmin === true) {
@@ -656,12 +915,12 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
656
915
  setError(null);
657
916
  }
658
917
  }, [isValidScope, organisationId, error, permission, pageId, isSuperAdmin]);
659
- const lastUserIdRef = useRef(null);
660
- const lastScopeRef = useRef(null);
661
- const lastPermissionRef = useRef(null);
662
- const lastPageIdRef = useRef(null);
663
- const lastUseCacheRef = useRef(null);
664
- 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(() => {
665
924
  if (!isValidScope) {
666
925
  return null;
667
926
  }
@@ -671,8 +930,8 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
671
930
  appId
672
931
  };
673
932
  }, [isValidScope, organisationId, eventId, appId]);
674
- const prevScopeRef = useRef(null);
675
- useEffect2(() => {
933
+ const prevScopeRef = useRef2(null);
934
+ useEffect5(() => {
676
935
  const scopeChanged = !scopeEqual(prevScopeRef.current, stableScope);
677
936
  if (lastUserIdRef.current !== userId || scopeChanged || lastPermissionRef.current !== permission || lastPageIdRef.current !== pageId || lastUseCacheRef.current !== useCache) {
678
937
  lastUserIdRef.current = userId;
@@ -683,7 +942,19 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
683
942
  const checkPermission = async () => {
684
943
  if (!userId) {
685
944
  setCan(false);
686
- 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);
687
958
  return;
688
959
  }
689
960
  if (!isValidScope) {
@@ -697,13 +968,10 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
697
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);
698
969
  const needsAppIdForPageName = isPagePermission && isPageName;
699
970
  if (requiresOrgId && (!organisationId || organisationId === null || typeof organisationId === "string" && organisationId.trim() === "")) {
700
- if (isSuperAdmin === true) {
701
- } else {
702
- setIsLoading(true);
703
- setCan(false);
704
- setError(null);
705
- return;
706
- }
971
+ setIsLoading(true);
972
+ setCan(false);
973
+ setError(null);
974
+ return;
707
975
  }
708
976
  if (needsAppIdForPageName && (!appId || appId === null || typeof appId === "string" && appId.trim() === "")) {
709
977
  setIsLoading(true);
@@ -719,11 +987,12 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
719
987
  ...eventId ? { eventId } : {},
720
988
  ...appId ? { appId } : {}
721
989
  };
722
- 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);
723
991
  setCan(result);
724
992
  } catch (err) {
725
993
  const logger2 = getRBACLogger();
726
994
  logger2.error("Permission check error:", { permission, error: err });
995
+ console.error("[useCan] Permission check error", { userId, permission, error: err });
727
996
  setError(err instanceof Error ? err : new Error("Failed to check permission"));
728
997
  setCan(false);
729
998
  } finally {
@@ -733,7 +1002,7 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
733
1002
  checkPermission();
734
1003
  }
735
1004
  }, [userId, stableScope, permission, pageId, useCache, appName, isSuperAdmin]);
736
- const refetch = useCallback2(async () => {
1005
+ const refetch = useCallback4(async () => {
737
1006
  if (!userId) {
738
1007
  setCan(false);
739
1008
  setIsLoading(false);
@@ -761,7 +1030,7 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
761
1030
  ...eventId ? { eventId } : {},
762
1031
  ...appId ? { appId } : {}
763
1032
  };
764
- 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);
765
1034
  setCan(result);
766
1035
  } catch (err) {
767
1036
  setError(err instanceof Error ? err : new Error("Failed to check permission"));
@@ -770,107 +1039,63 @@ function useCan(userId, scope, permission, pageId, useCache = true, appName) {
770
1039
  setIsLoading(false);
771
1040
  }
772
1041
  }, [userId, isValidScope, organisationId, eventId, appId, permission, pageId, useCache, appName]);
773
- return useMemo2(() => ({
1042
+ return useMemo5(() => ({
774
1043
  can,
775
1044
  isLoading,
776
1045
  error,
777
1046
  refetch
778
1047
  }), [can, isLoading, error, refetch]);
779
1048
  }
780
- function useAccessLevel(userId, scope) {
781
- const [accessLevel, setAccessLevel] = useState2("viewer");
782
- const [isLoading, setIsLoading] = useState2(true);
783
- const [error, setError] = useState2(null);
784
- let appName;
785
- try {
786
- const { appName: contextAppName } = useAppConfig();
787
- appName = contextAppName;
788
- } catch {
789
- }
790
- const fetchAccessLevel = useCallback2(async () => {
791
- if (!userId) {
792
- setAccessLevel("viewer");
793
- setIsLoading(false);
794
- return;
795
- }
796
- try {
797
- setIsLoading(true);
798
- setError(null);
799
- const { isSuperAdmin: checkSuperAdmin } = await import("./api-N774RPUA.js");
800
- const isSuperAdminUser = await checkSuperAdmin(userId);
801
- if (isSuperAdminUser) {
802
- setAccessLevel("super");
803
- setIsLoading(false);
804
- return;
805
- }
806
- if (appName !== "PORTAL" && appName !== "ADMIN" && !scope.organisationId && !scope.eventId) {
807
- const orgError = new OrganisationContextRequiredError();
808
- setError(orgError);
809
- setAccessLevel("viewer");
810
- setIsLoading(false);
811
- return;
812
- }
813
- const level = await getAccessLevel({ userId, scope }, null, appName);
814
- setAccessLevel(level);
815
- } catch (err) {
816
- const error2 = err instanceof Error ? err : new Error("Failed to fetch access level");
817
- setError(error2);
818
- setAccessLevel("viewer");
819
- } finally {
820
- setIsLoading(false);
821
- }
822
- }, [userId, scope.organisationId, scope.eventId, scope.appId, appName]);
823
- useEffect2(() => {
824
- fetchAccessLevel();
825
- }, [fetchAccessLevel]);
826
- return useMemo2(() => ({
827
- accessLevel,
828
- isLoading,
829
- error,
830
- refetch: fetchAccessLevel
831
- }), [accessLevel, isLoading, error, fetchAccessLevel]);
832
- }
833
- function useMultiplePermissions(userId, scope, permissions, useCache = true) {
834
- const [results, setResults] = useState2({});
835
- const [isLoading, setIsLoading] = useState2(true);
836
- const [error, setError] = useState2(null);
837
- 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 () => {
838
1057
  if (!userId || permissions.length === 0) {
839
- setResults({});
1058
+ setHasAll(false);
840
1059
  setIsLoading(false);
841
1060
  return;
842
1061
  }
843
1062
  try {
844
1063
  setIsLoading(true);
845
1064
  setError(null);
846
- const permissionResults = {};
1065
+ let hasAllPermissions = true;
847
1066
  for (const permission of permissions) {
848
- const result = useCache ? await isPermittedCached({ userId, scope, permission }) : await isPermitted({ userId, scope, permission });
849
- 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
+ }
850
1072
  }
851
- setResults(permissionResults);
1073
+ setHasAll(hasAllPermissions);
852
1074
  } catch (err) {
853
1075
  setError(err instanceof Error ? err : new Error("Failed to check permissions"));
854
- setResults({});
1076
+ setHasAll(false);
855
1077
  } finally {
856
1078
  setIsLoading(false);
857
1079
  }
858
1080
  }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);
859
- useEffect2(() => {
860
- checkPermissions();
861
- }, [checkPermissions]);
862
- return useMemo2(() => ({
863
- results,
1081
+ useEffect6(() => {
1082
+ checkAllPermissions();
1083
+ }, [checkAllPermissions]);
1084
+ return useMemo6(() => ({
1085
+ hasAll,
864
1086
  isLoading,
865
1087
  error,
866
- refetch: checkPermissions
867
- }), [results, isLoading, error, checkPermissions]);
1088
+ refetch: checkAllPermissions
1089
+ }), [hasAll, isLoading, error, checkAllPermissions]);
868
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";
869
1094
  function useHasAnyPermission(userId, scope, permissions, useCache = true) {
870
- const [hasAny, setHasAny] = useState2(false);
871
- const [isLoading, setIsLoading] = useState2(true);
872
- const [error, setError] = useState2(null);
873
- 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 () => {
874
1099
  if (!userId || permissions.length === 0) {
875
1100
  setHasAny(false);
876
1101
  setIsLoading(false);
@@ -881,7 +1106,7 @@ function useHasAnyPermission(userId, scope, permissions, useCache = true) {
881
1106
  setError(null);
882
1107
  let hasAnyPermission = false;
883
1108
  for (const permission of permissions) {
884
- 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);
885
1110
  if (result) {
886
1111
  hasAnyPermission = true;
887
1112
  break;
@@ -895,93 +1120,203 @@ function useHasAnyPermission(userId, scope, permissions, useCache = true) {
895
1120
  setIsLoading(false);
896
1121
  }
897
1122
  }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);
898
- useEffect2(() => {
1123
+ useEffect7(() => {
899
1124
  checkAnyPermission();
900
1125
  }, [checkAnyPermission]);
901
- return useMemo2(() => ({
1126
+ return useMemo7(() => ({
902
1127
  hasAny,
903
1128
  isLoading,
904
1129
  error,
905
1130
  refetch: checkAnyPermission
906
1131
  }), [hasAny, isLoading, error, checkAnyPermission]);
907
1132
  }
908
- function useHasAllPermissions(userId, scope, permissions, useCache = true) {
909
- const [hasAll, setHasAll] = useState2(false);
910
- const [isLoading, setIsLoading] = useState2(true);
911
- const [error, setError] = useState2(null);
912
- 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 () => {
913
1141
  if (!userId || permissions.length === 0) {
914
- setHasAll(false);
1142
+ setResults({});
915
1143
  setIsLoading(false);
916
1144
  return;
917
1145
  }
918
1146
  try {
919
1147
  setIsLoading(true);
920
1148
  setError(null);
921
- let hasAllPermissions = true;
1149
+ const permissionResults = {};
922
1150
  for (const permission of permissions) {
923
- const result = useCache ? await isPermittedCached({ userId, scope, permission }) : await isPermitted({ userId, scope, permission });
924
- if (!result) {
925
- hasAllPermissions = false;
926
- break;
927
- }
1151
+ const result = useCache ? await isPermittedCached({ userId, scope, permission }) : await isPermitted({ userId, scope, permission }, null, void 0, null);
1152
+ permissionResults[permission] = result;
928
1153
  }
929
- setHasAll(hasAllPermissions);
1154
+ setResults(permissionResults);
930
1155
  } catch (err) {
931
1156
  setError(err instanceof Error ? err : new Error("Failed to check permissions"));
932
- setHasAll(false);
1157
+ setResults({});
933
1158
  } finally {
934
1159
  setIsLoading(false);
935
1160
  }
936
1161
  }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);
937
- useEffect2(() => {
938
- checkAllPermissions();
939
- }, [checkAllPermissions]);
940
- return useMemo2(() => ({
941
- hasAll,
1162
+ useEffect8(() => {
1163
+ checkPermissions();
1164
+ }, [checkPermissions]);
1165
+ return useMemo8(() => ({
1166
+ results,
942
1167
  isLoading,
943
1168
  error,
944
- refetch: checkAllPermissions
945
- }), [hasAll, isLoading, error, checkAllPermissions]);
1169
+ refetch: checkPermissions
1170
+ }), [results, isLoading, error, checkPermissions]);
946
1171
  }
947
- function useCachedPermissions(userId, scope) {
948
- const [permissions, setPermissions] = useState2({});
949
- const [isLoading, setIsLoading] = useState2(true);
950
- const [error, setError] = useState2(null);
951
- 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
+ }
952
1277
  if (!userId) {
953
1278
  setPermissions({});
954
1279
  setIsLoading(false);
955
1280
  return;
956
1281
  }
1282
+ if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
1283
+ setIsLoading(true);
1284
+ setError(null);
1285
+ return;
1286
+ }
957
1287
  try {
1288
+ isFetchingRef.current = true;
958
1289
  setIsLoading(true);
959
1290
  setError(null);
1291
+ const scope = {
1292
+ organisationId: orgId,
1293
+ eventId,
1294
+ appId
1295
+ };
960
1296
  const permissionMap = await getPermissionMap({ userId, scope });
961
1297
  setPermissions(permissionMap);
962
1298
  } catch (err) {
963
- 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"));
964
1302
  } finally {
965
1303
  setIsLoading(false);
1304
+ isFetchingRef.current = false;
966
1305
  }
967
- }, [userId, scope.organisationId, scope.eventId, scope.appId]);
968
- const invalidateCache = useCallback2(() => {
969
- fetchCachedPermissions();
970
- }, [fetchCachedPermissions]);
971
- useEffect2(() => {
972
- fetchCachedPermissions();
973
- }, [fetchCachedPermissions]);
974
- return useMemo2(() => ({
1306
+ }, [userId, organisationId, eventId, appId]);
1307
+ return useMemo9(() => ({
975
1308
  permissions,
976
1309
  isLoading,
977
1310
  error,
978
- invalidateCache,
979
- refetch: fetchCachedPermissions
980
- }), [permissions, isLoading, error, invalidateCache, fetchCachedPermissions]);
1311
+ hasPermission,
1312
+ hasAnyPermission,
1313
+ hasAllPermissions,
1314
+ refetch
1315
+ }), [permissions, isLoading, error, hasPermission, hasAnyPermission, hasAllPermissions, refetch]);
981
1316
  }
982
1317
 
983
1318
  // src/rbac/hooks/useResourcePermissions.ts
984
- import { useMemo as useMemo3 } from "react";
1319
+ import { useMemo as useMemo10 } from "react";
985
1320
  function useResourcePermissions(resource, options = {}) {
986
1321
  const { enableRead = false, requireScope = true } = options;
987
1322
  const { user, supabase } = useUnifiedAuth();
@@ -1009,8 +1344,12 @@ function useResourcePermissions(resource, options = {}) {
1009
1344
  `create:${resource}`,
1010
1345
  pageId,
1011
1346
  // Pass resource name as pageId when appId is available to enable page permission checks
1012
- true
1347
+ true,
1013
1348
  // useCache
1349
+ null,
1350
+ // precomputedSuperAdmin - not checked yet
1351
+ void 0
1352
+ // appName
1014
1353
  );
1015
1354
  const { can: canUpdateResult, isLoading: updateLoading, error: updateError } = useCan(
1016
1355
  user?.id || "",
@@ -1018,8 +1357,12 @@ function useResourcePermissions(resource, options = {}) {
1018
1357
  `update:${resource}`,
1019
1358
  pageId,
1020
1359
  // Pass resource name as pageId when appId is available to enable page permission checks
1021
- true
1360
+ true,
1022
1361
  // useCache
1362
+ null,
1363
+ // precomputedSuperAdmin - not checked yet
1364
+ void 0
1365
+ // appName
1023
1366
  );
1024
1367
  const { can: canDeleteResult, isLoading: deleteLoading, error: deleteError } = useCan(
1025
1368
  user?.id || "",
@@ -1027,8 +1370,12 @@ function useResourcePermissions(resource, options = {}) {
1027
1370
  `delete:${resource}`,
1028
1371
  pageId,
1029
1372
  // Pass resource name as pageId when appId is available to enable page permission checks
1030
- true
1373
+ true,
1031
1374
  // useCache
1375
+ null,
1376
+ // precomputedSuperAdmin - not checked yet
1377
+ void 0
1378
+ // appName
1032
1379
  );
1033
1380
  const { can: canReadResult, isLoading: readLoading, error: readError } = useCan(
1034
1381
  user?.id || "",
@@ -1036,13 +1383,17 @@ function useResourcePermissions(resource, options = {}) {
1036
1383
  `read:${resource}`,
1037
1384
  pageId,
1038
1385
  // Pass resource name as pageId when appId is available to enable page permission checks
1039
- true
1386
+ true,
1040
1387
  // useCache
1388
+ null,
1389
+ // precomputedSuperAdmin - not checked yet
1390
+ void 0
1391
+ // appName
1041
1392
  );
1042
- const isLoading = useMemo3(() => {
1393
+ const isLoading = useMemo10(() => {
1043
1394
  return scopeLoading || createLoading || updateLoading || deleteLoading || enableRead && readLoading;
1044
1395
  }, [scopeLoading, createLoading, updateLoading, deleteLoading, readLoading, enableRead]);
1045
- const error = useMemo3(() => {
1396
+ const error = useMemo10(() => {
1046
1397
  if (scopeError) return scopeError;
1047
1398
  if (createError) return createError;
1048
1399
  if (updateError) return updateError;
@@ -1050,7 +1401,7 @@ function useResourcePermissions(resource, options = {}) {
1050
1401
  if (enableRead && readError) return readError;
1051
1402
  return null;
1052
1403
  }, [scopeError, createError, updateError, deleteError, readError, enableRead]);
1053
- return useMemo3(() => ({
1404
+ return useMemo10(() => ({
1054
1405
  canCreate: (res) => {
1055
1406
  if (res !== resource) {
1056
1407
  return false;
@@ -1095,15 +1446,15 @@ function useResourcePermissions(resource, options = {}) {
1095
1446
  }
1096
1447
 
1097
1448
  // src/rbac/hooks/useRoleManagement.ts
1098
- import { useState as useState3, useCallback as useCallback3 } from "react";
1449
+ import { useState as useState10, useCallback as useCallback9 } from "react";
1099
1450
  function useRoleManagement() {
1100
1451
  const { user, supabase } = useUnifiedAuth();
1101
- const [isLoading, setIsLoading] = useState3(false);
1102
- const [error, setError] = useState3(null);
1452
+ const [isLoading, setIsLoading] = useState10(false);
1453
+ const [error, setError] = useState10(null);
1103
1454
  if (!supabase) {
1104
1455
  throw new Error("useRoleManagement requires a Supabase client. Ensure UnifiedAuthProvider is configured.");
1105
1456
  }
1106
- const revokeEventAppRole = useCallback3(async (params) => {
1457
+ const revokeEventAppRole = useCallback9(async (params) => {
1107
1458
  setIsLoading(true);
1108
1459
  setError(null);
1109
1460
  try {
@@ -1134,7 +1485,7 @@ function useRoleManagement() {
1134
1485
  setIsLoading(false);
1135
1486
  }
1136
1487
  }, [user?.id]);
1137
- const grantEventAppRole = useCallback3(async (params) => {
1488
+ const grantEventAppRole = useCallback9(async (params) => {
1138
1489
  setIsLoading(true);
1139
1490
  setError(null);
1140
1491
  try {
@@ -1173,7 +1524,7 @@ function useRoleManagement() {
1173
1524
  setIsLoading(false);
1174
1525
  }
1175
1526
  }, [user?.id]);
1176
- const revokeRoleById = useCallback3(async (roleId) => {
1527
+ const revokeRoleById = useCallback9(async (roleId) => {
1177
1528
  setIsLoading(true);
1178
1529
  setError(null);
1179
1530
  try {
@@ -1209,7 +1560,7 @@ function useRoleManagement() {
1209
1560
  setIsLoading(false);
1210
1561
  }
1211
1562
  }, [user?.id, supabase]);
1212
- const grantGlobalRole = useCallback3(async (params) => {
1563
+ const grantGlobalRole = useCallback9(async (params) => {
1213
1564
  setIsLoading(true);
1214
1565
  setError(null);
1215
1566
  try {
@@ -1248,7 +1599,7 @@ function useRoleManagement() {
1248
1599
  setIsLoading(false);
1249
1600
  }
1250
1601
  }, [user?.id, supabase]);
1251
- const revokeGlobalRole = useCallback3(async (params) => {
1602
+ const revokeGlobalRole = useCallback9(async (params) => {
1252
1603
  setIsLoading(true);
1253
1604
  setError(null);
1254
1605
  try {
@@ -1280,7 +1631,7 @@ function useRoleManagement() {
1280
1631
  setIsLoading(false);
1281
1632
  }
1282
1633
  }, [user?.id, supabase]);
1283
- const grantOrganisationRole = useCallback3(async (params) => {
1634
+ const grantOrganisationRole = useCallback9(async (params) => {
1284
1635
  setIsLoading(true);
1285
1636
  setError(null);
1286
1637
  try {
@@ -1319,7 +1670,7 @@ function useRoleManagement() {
1319
1670
  setIsLoading(false);
1320
1671
  }
1321
1672
  }, [user?.id, supabase]);
1322
- const revokeOrganisationRole = useCallback3(async (params) => {
1673
+ const revokeOrganisationRole = useCallback9(async (params) => {
1323
1674
  setIsLoading(true);
1324
1675
  setError(null);
1325
1676
  try {
@@ -1369,11 +1720,11 @@ function useRoleManagement() {
1369
1720
  }
1370
1721
 
1371
1722
  // src/rbac/hooks/useSecureSupabase.ts
1372
- import { useMemo as useMemo4, useRef as useRef2 } from "react";
1723
+ import { useMemo as useMemo11, useRef as useRef4 } from "react";
1373
1724
  var secureClientCache = /* @__PURE__ */ new Map();
1374
1725
  var MAX_CACHE_SIZE = 5;
1375
1726
  function getCacheKey(organisationId, eventId, appId, isSuperAdmin) {
1376
- 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"}`;
1377
1728
  }
1378
1729
  function getSupabaseConfig() {
1379
1730
  const getEnvVar = (key) => {
@@ -1405,19 +1756,20 @@ function useSecureSupabase(baseClient) {
1405
1756
  selectedOrganisationId: selectedOrganisation?.id || null,
1406
1757
  selectedEventId: selectedEvent?.event_id || null
1407
1758
  });
1408
- const prevContextRef = useRef2({
1759
+ const prevContextRef = useRef4({
1409
1760
  organisationId: void 0,
1410
1761
  eventId: void 0,
1411
1762
  appId: void 0
1412
1763
  });
1413
- return useMemo4(() => {
1764
+ return useMemo11(() => {
1414
1765
  if (eventLoading) {
1415
1766
  return baseClient || authSupabase || null;
1416
1767
  }
1417
1768
  const organisationId = resolvedScope?.organisationId;
1418
1769
  const eventId = resolvedScope?.eventId || selectedEvent?.event_id;
1419
1770
  const appId = resolvedScope?.appId;
1420
- if (organisationId && user?.id) {
1771
+ const canCreateSecureClient = user?.id && (isSuperAdmin || organisationId);
1772
+ if (canCreateSecureClient) {
1421
1773
  prevContextRef.current = { organisationId, eventId, appId };
1422
1774
  const cacheKey = getCacheKey(organisationId, eventId, appId, isSuperAdmin);
1423
1775
  const cachedClient = secureClientCache.get(cacheKey);
@@ -1432,11 +1784,19 @@ function useSecureSupabase(baseClient) {
1432
1784
  return baseClient || authSupabase || null;
1433
1785
  }
1434
1786
  try {
1435
- 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(
1436
1796
  config.url,
1437
1797
  config.key,
1438
- organisationId,
1439
- // organisationId is string, UUID is string alias
1798
+ effectiveOrganisationId,
1799
+ // organisationId is string | null, UUID is string alias
1440
1800
  eventId,
1441
1801
  appId,
1442
1802
  // appId is string | undefined, UUID is string alias
@@ -1474,17 +1834,18 @@ export {
1474
1834
  SecureSupabaseClient,
1475
1835
  createSecureClient,
1476
1836
  fromSupabaseClient,
1837
+ useResolvedScope,
1477
1838
  useRBAC,
1839
+ useAccessLevel,
1840
+ useCachedPermissions,
1478
1841
  scopeEqual,
1479
- usePermissions,
1480
1842
  useCan,
1481
- useAccessLevel,
1482
- useMultiplePermissions,
1483
- useHasAnyPermission,
1484
1843
  useHasAllPermissions,
1485
- useCachedPermissions,
1844
+ useHasAnyPermission,
1845
+ useMultiplePermissions,
1846
+ usePermissions,
1486
1847
  useResourcePermissions,
1487
1848
  useRoleManagement,
1488
1849
  useSecureSupabase
1489
1850
  };
1490
- //# sourceMappingURL=chunk-XM25TVIE.js.map
1851
+ //# sourceMappingURL=chunk-XWQCNGTQ.js.map