@jmruthers/pace-core 0.5.190 → 0.5.193

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 (334) hide show
  1. package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
  2. package/dist/{DataTable-ON3IXISJ.js → DataTable-5FU7IESH.js} +7 -6
  3. package/dist/{DataTable-IVYljGJ6.d.ts → DataTable-Be6dH_dR.d.ts} +1 -1
  4. package/dist/{PublicPageProvider-C4uxosp6.d.ts → PublicPageProvider-C0Sm_e5k.d.ts} +4 -2
  5. package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
  6. package/dist/{UnifiedAuthProvider-X5NXANVI.js → UnifiedAuthProvider-RGJTDE2C.js} +3 -3
  7. package/dist/{api-I6UCQ5S6.js → api-N774RPUA.js} +2 -2
  8. package/dist/chunk-6C4YBBJM 5.js +628 -0
  9. package/dist/chunk-7D4SUZUM.js 2.map +1 -0
  10. package/dist/{chunk-73HSNNOQ.js → chunk-7EQTDTTJ.js} +47 -74
  11. package/dist/chunk-7EQTDTTJ.js 2.map +1 -0
  12. package/dist/chunk-7EQTDTTJ.js.map +1 -0
  13. package/dist/{chunk-J2XXC7R5.js → chunk-7FLMSG37.js} +409 -244
  14. package/dist/chunk-7FLMSG37.js 2.map +1 -0
  15. package/dist/chunk-7FLMSG37.js.map +1 -0
  16. package/dist/{chunk-NIU6J6OX.js → chunk-BC4IJKSL.js} +23 -32
  17. package/dist/chunk-BC4IJKSL.js.map +1 -0
  18. package/dist/{chunk-SDMHPX3X.js → chunk-E3SPN4VZ 5.js } +198 -53
  19. package/dist/chunk-E3SPN4VZ.js +12917 -0
  20. package/dist/{chunk-SDMHPX3X.js.map → chunk-E3SPN4VZ.js.map} +1 -1
  21. package/dist/chunk-E66EQZE6 5.js +37 -0
  22. package/dist/chunk-E66EQZE6.js 2.map +1 -0
  23. package/dist/{chunk-DZWK57KZ.js → chunk-G37KK66H.js} +1 -1
  24. package/dist/{chunk-DZWK57KZ.js.map → chunk-G37KK66H.js.map} +1 -1
  25. package/dist/{chunk-STYK4OH2.js → chunk-HWIIPPNI.js} +44 -225
  26. package/dist/chunk-HWIIPPNI.js.map +1 -0
  27. package/dist/chunk-I7PSE6JW 5.js +191 -0
  28. package/dist/chunk-I7PSE6JW.js 2.map +1 -0
  29. package/dist/{chunk-Y4BUBBHD.js → chunk-IIELH4DL.js} +211 -136
  30. package/dist/chunk-IIELH4DL.js.map +1 -0
  31. package/dist/{chunk-RUYZKXOD.js → chunk-KNC55RTG.js} +17 -5
  32. package/dist/chunk-KNC55RTG.js 5.map +1 -0
  33. package/dist/chunk-KNC55RTG.js.map +1 -0
  34. package/dist/chunk-KQCRWDSA.js 5.map +1 -0
  35. package/dist/{chunk-4QYC5L4K.js → chunk-LFNCN2SP.js} +26 -30
  36. package/dist/chunk-LFNCN2SP.js 2.map +1 -0
  37. package/dist/chunk-LFNCN2SP.js.map +1 -0
  38. package/dist/chunk-LMC26NLJ 2.js +84 -0
  39. package/dist/{chunk-VVBAW5A5.js → chunk-NOAYCWCX 5.js } +118 -110
  40. package/dist/chunk-NOAYCWCX.js +4993 -0
  41. package/dist/chunk-NOAYCWCX.js.map +1 -0
  42. package/dist/chunk-QWWZ5CAQ.js 3.map +1 -0
  43. package/dist/chunk-QXHPKYJV 3.js +113 -0
  44. package/dist/chunk-R77UEZ4E 3.js +68 -0
  45. package/dist/chunk-VBXEHIUJ.js 6.map +1 -0
  46. package/dist/{chunk-HQVPB5MZ.js → chunk-XNXXZ43G.js} +77 -33
  47. package/dist/chunk-XNXXZ43G.js.map +1 -0
  48. package/dist/chunk-ZSAAAMVR 6.js +25 -0
  49. package/dist/components.d.ts +4 -4
  50. package/dist/components.js +8 -8
  51. package/dist/components.js 5.map +1 -0
  52. package/dist/{database.generated-DI89OQeI.d.ts → database.generated-CzIvgcPu.d.ts} +165 -201
  53. package/dist/hooks.d.ts +12 -12
  54. package/dist/hooks.js +9 -9
  55. package/dist/index.d.ts +11 -11
  56. package/dist/index.js +20 -27
  57. package/dist/index.js.map +1 -1
  58. package/dist/providers.d.ts +3 -3
  59. package/dist/providers.js +2 -2
  60. package/dist/rbac/index.d.ts +2 -20
  61. package/dist/rbac/index.js +7 -9
  62. package/dist/styles/index 2.js +12 -0
  63. package/dist/styles/index.js 5.map +1 -0
  64. package/dist/theming/runtime 5.js +19 -0
  65. package/dist/theming/runtime.js 5.map +1 -0
  66. package/dist/{types-Bwgl--Xo.d.ts → types-CEpcvwwF.d.ts} +1 -1
  67. package/dist/types.d.ts +2 -2
  68. package/dist/{usePublicRouteParams-DxIDS4bC.d.ts → usePublicRouteParams-TZe0gy-4.d.ts} +1 -1
  69. package/dist/utils.d.ts +8 -8
  70. package/dist/utils.js +2 -2
  71. package/docs/api/classes/ColumnFactory.md +1 -1
  72. package/docs/api/classes/ErrorBoundary.md +1 -1
  73. package/docs/api/classes/InvalidScopeError.md +1 -1
  74. package/docs/api/classes/Logger.md +1 -1
  75. package/docs/api/classes/MissingUserContextError.md +1 -1
  76. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  77. package/docs/api/classes/PermissionDeniedError.md +2 -2
  78. package/docs/api/classes/RBACAuditManager.md +2 -2
  79. package/docs/api/classes/RBACCache.md +1 -1
  80. package/docs/api/classes/RBACEngine.md +2 -2
  81. package/docs/api/classes/RBACError.md +1 -1
  82. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  83. package/docs/api/classes/SecureSupabaseClient.md +10 -10
  84. package/docs/api/classes/StorageUtils.md +1 -1
  85. package/docs/api/enums/FileCategory.md +1 -1
  86. package/docs/api/enums/LogLevel.md +1 -1
  87. package/docs/api/enums/RBACErrorCode.md +1 -1
  88. package/docs/api/enums/RPCFunction.md +1 -1
  89. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  90. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  91. package/docs/api/interfaces/AggregateConfig.md +1 -1
  92. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  93. package/docs/api/interfaces/AvatarProps.md +1 -1
  94. package/docs/api/interfaces/BadgeProps.md +1 -1
  95. package/docs/api/interfaces/ButtonProps.md +1 -1
  96. package/docs/api/interfaces/CalendarProps.md +1 -1
  97. package/docs/api/interfaces/CardProps.md +1 -1
  98. package/docs/api/interfaces/ColorPalette.md +1 -1
  99. package/docs/api/interfaces/ColorShade.md +1 -1
  100. package/docs/api/interfaces/ComplianceResult.md +1 -1
  101. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  102. package/docs/api/interfaces/DataRecord.md +1 -1
  103. package/docs/api/interfaces/DataTableAction.md +1 -1
  104. package/docs/api/interfaces/DataTableColumn.md +1 -1
  105. package/docs/api/interfaces/DataTableProps.md +1 -1
  106. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  107. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  108. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  109. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  110. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  111. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  112. package/docs/api/interfaces/ExportColumn.md +1 -1
  113. package/docs/api/interfaces/ExportOptions.md +1 -1
  114. package/docs/api/interfaces/FileDisplayProps.md +24 -11
  115. package/docs/api/interfaces/FileMetadata.md +1 -1
  116. package/docs/api/interfaces/FileReference.md +1 -1
  117. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  118. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  119. package/docs/api/interfaces/FileUploadProps.md +1 -1
  120. package/docs/api/interfaces/FooterProps.md +1 -1
  121. package/docs/api/interfaces/FormFieldProps.md +1 -1
  122. package/docs/api/interfaces/FormProps.md +1 -1
  123. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  124. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  125. package/docs/api/interfaces/InputProps.md +1 -1
  126. package/docs/api/interfaces/LabelProps.md +1 -1
  127. package/docs/api/interfaces/LoggerConfig.md +1 -1
  128. package/docs/api/interfaces/LoginFormProps.md +1 -1
  129. package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
  130. package/docs/api/interfaces/NavigationContextType.md +1 -1
  131. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  132. package/docs/api/interfaces/NavigationItem.md +1 -1
  133. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  134. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  135. package/docs/api/interfaces/Organisation.md +1 -1
  136. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  137. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  138. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  139. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  140. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  141. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  142. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  143. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  144. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  145. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  146. package/docs/api/interfaces/PaletteData.md +1 -1
  147. package/docs/api/interfaces/ParsedAddress.md +2 -2
  148. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  149. package/docs/api/interfaces/ProgressProps.md +1 -1
  150. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  151. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  152. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  153. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  154. package/docs/api/interfaces/QuickFix.md +1 -1
  155. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  156. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  157. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  158. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  159. package/docs/api/interfaces/RBACConfig.md +2 -2
  160. package/docs/api/interfaces/RBACContext.md +1 -1
  161. package/docs/api/interfaces/RBACLogger.md +1 -1
  162. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  163. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  164. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  165. package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
  166. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  167. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  168. package/docs/api/interfaces/RBACResult.md +1 -1
  169. package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
  170. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  171. package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
  172. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  173. package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
  174. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  175. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  176. package/docs/api/interfaces/RBACRolesListResult.md +2 -2
  177. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  178. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  179. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  180. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  181. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  182. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  183. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  184. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  185. package/docs/api/interfaces/RouteConfig.md +2 -2
  186. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  187. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  188. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  189. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  190. package/docs/api/interfaces/SetupIssue.md +1 -1
  191. package/docs/api/interfaces/StorageConfig.md +1 -1
  192. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  193. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  194. package/docs/api/interfaces/StorageListOptions.md +1 -1
  195. package/docs/api/interfaces/StorageListResult.md +1 -1
  196. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  197. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  198. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  199. package/docs/api/interfaces/StyleImport.md +1 -1
  200. package/docs/api/interfaces/SwitchProps.md +1 -1
  201. package/docs/api/interfaces/TabsContentProps.md +1 -1
  202. package/docs/api/interfaces/TabsListProps.md +1 -1
  203. package/docs/api/interfaces/TabsProps.md +1 -1
  204. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  205. package/docs/api/interfaces/TextareaProps.md +1 -1
  206. package/docs/api/interfaces/ToastActionElement.md +1 -1
  207. package/docs/api/interfaces/ToastProps.md +1 -1
  208. package/docs/api/interfaces/UnifiedAuthContextType.md +60 -38
  209. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  210. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  211. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  212. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  213. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  214. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  215. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  216. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  217. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  218. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  219. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  220. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  221. package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
  222. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  223. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  224. package/docs/api/interfaces/UserEventAccess.md +1 -1
  225. package/docs/api/interfaces/UserMenuProps.md +1 -1
  226. package/docs/api/interfaces/UserProfile.md +1 -1
  227. package/docs/api/modules.md +202 -217
  228. package/docs/migration/README.md +18 -0
  229. package/docs/migration/database-changes-december-2025.md +768 -0
  230. package/docs/migration/person-scoped-profiles-migration-guide.md +472 -0
  231. package/docs/rbac/event-based-apps.md +124 -6
  232. package/package.json +1 -1
  233. package/scripts/check-pace-core-compliance.cjs +292 -57
  234. package/src/__tests__/public-recipe-view.test.ts +10 -10
  235. package/src/__tests__/rls-policies.test.ts +16 -14
  236. package/src/components/AddressField/README.md +6 -6
  237. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
  238. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
  239. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
  240. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
  241. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
  242. package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
  243. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +75 -11
  244. package/src/components/DataTable/components/UnifiedTableBody.tsx +85 -14
  245. package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
  246. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -1
  247. package/src/components/FileDisplay/FileDisplay.tsx +16 -4
  248. package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
  249. package/src/components/NavigationMenu/NavigationMenu.tsx +1 -10
  250. package/src/components/OrganisationSelector/OrganisationSelector.tsx +35 -16
  251. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +25 -2
  252. package/src/components/PaceAppLayout/PaceAppLayout.tsx +97 -68
  253. package/src/components/PaceLoginPage/PaceLoginPage.tsx +0 -7
  254. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
  255. package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
  256. package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
  257. package/src/components/Select/Select.test.tsx +4 -1
  258. package/src/components/Select/Select.tsx +60 -15
  259. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +192 -0
  260. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +741 -0
  261. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +703 -0
  262. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +581 -0
  263. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +23 -15
  264. package/src/hooks/public/usePublicEvent.ts +8 -8
  265. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  266. package/src/hooks/services/useAuthService.ts +21 -3
  267. package/src/hooks/services/useEventService.ts +21 -3
  268. package/src/hooks/services/useInactivityService.ts +21 -3
  269. package/src/hooks/services/useOrganisationService.ts +21 -3
  270. package/src/hooks/useFileDisplay.ts +18 -26
  271. package/src/hooks/useQueryCache.ts +6 -6
  272. package/src/hooks/useSecureDataAccess.test.ts +24 -17
  273. package/src/hooks/useSecureDataAccess.ts +18 -13
  274. package/src/providers/__tests__/OrganisationProvider.test.tsx +27 -21
  275. package/src/providers/services/EventServiceProvider.tsx +0 -8
  276. package/src/providers/services/UnifiedAuthProvider.tsx +174 -24
  277. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +10 -16
  278. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
  279. package/src/rbac/adapters.tsx +3 -22
  280. package/src/rbac/api.test.ts +2 -2
  281. package/src/rbac/api.ts +7 -1
  282. package/src/rbac/components/EnhancedNavigationMenu.tsx +2 -15
  283. package/src/rbac/components/NavigationGuard.tsx +1 -10
  284. package/src/rbac/components/NavigationProvider.tsx +0 -1
  285. package/src/rbac/components/PermissionEnforcer.tsx +45 -12
  286. package/src/rbac/components/SecureDataProvider.tsx +0 -1
  287. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
  288. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
  289. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
  290. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
  291. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
  292. package/src/rbac/engine.ts +14 -2
  293. package/src/rbac/hooks/index.ts +0 -3
  294. package/src/rbac/hooks/usePermissions.ts +51 -11
  295. package/src/rbac/hooks/useRBAC.simple.test.ts +95 -0
  296. package/src/rbac/hooks/useRBAC.ts +3 -13
  297. package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
  298. package/src/rbac/hooks/useResolvedScope.ts +58 -33
  299. package/src/rbac/hooks/useSecureSupabase.ts +4 -9
  300. package/src/rbac/secureClient.ts +31 -0
  301. package/src/rbac/utils/__tests__/eventContext.test.ts +2 -2
  302. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +490 -0
  303. package/src/rbac/utils/eventContext.ts +5 -2
  304. package/src/services/AuthService.ts +37 -8
  305. package/src/services/EventService.ts +4 -57
  306. package/src/services/InactivityService.ts +127 -34
  307. package/src/services/OrganisationService.ts +160 -149
  308. package/src/services/__tests__/OrganisationService.pagination.test.ts +34 -8
  309. package/src/services/__tests__/OrganisationService.test.ts +218 -86
  310. package/src/types/database.generated.ts +166 -201
  311. package/src/types/supabase.ts +2 -2
  312. package/src/utils/__tests__/secureDataAccess.unit.test.ts +3 -2
  313. package/src/utils/file-reference/index.ts +4 -4
  314. package/src/utils/google-places/googlePlacesUtils.ts +1 -1
  315. package/src/utils/google-places/types.ts +1 -1
  316. package/src/utils/request-deduplication.ts +4 -4
  317. package/src/utils/security/secureDataAccess.test.ts +1 -1
  318. package/src/utils/security/secureDataAccess.ts +7 -4
  319. package/src/utils/storage/README.md +1 -1
  320. package/dist/chunk-4QYC5L4K.js.map +0 -1
  321. package/dist/chunk-73HSNNOQ.js.map +0 -1
  322. package/dist/chunk-HQVPB5MZ.js.map +0 -1
  323. package/dist/chunk-J2XXC7R5.js.map +0 -1
  324. package/dist/chunk-NIU6J6OX.js.map +0 -1
  325. package/dist/chunk-RUYZKXOD.js.map +0 -1
  326. package/dist/chunk-STYK4OH2.js.map +0 -1
  327. package/dist/chunk-VVBAW5A5.js.map +0 -1
  328. package/dist/chunk-Y4BUBBHD.js.map +0 -1
  329. package/scripts/check-pace-core-compliance.js +0 -512
  330. package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
  331. package/src/utils/context/superAdminOverride.ts +0 -58
  332. /package/dist/{DataTable-ON3IXISJ.js.map → DataTable-5FU7IESH.js.map} +0 -0
  333. /package/dist/{UnifiedAuthProvider-X5NXANVI.js.map → UnifiedAuthProvider-RGJTDE2C.js.map} +0 -0
  334. /package/dist/{api-I6UCQ5S6.js.map → api-N774RPUA.js.map} +0 -0
@@ -20,6 +20,8 @@ import type {
20
20
  import { setOrganisationContext } from '../utils/context/organisationContext';
21
21
  import { logger } from '../utils/core/logger';
22
22
  import { assertUserId, assertOrganisationId } from '../types/core';
23
+ import { isSuperAdmin } from '../rbac/api';
24
+ import type { UUID } from '../rbac/types';
23
25
 
24
26
  // Type for RPC response from data_user_organisation_roles_get
25
27
  interface OrganisationRoleRpcResponse {
@@ -27,6 +29,15 @@ interface OrganisationRoleRpcResponse {
27
29
  organisation_id: string;
28
30
  role: 'org_admin' | 'leader' | 'member' | 'supporter';
29
31
  status: 'active' | 'inactive' | 'suspended';
32
+ // Organisation fields from RPC
33
+ name?: string;
34
+ display_name?: string;
35
+ subscription_tier?: string;
36
+ settings?: unknown;
37
+ is_active?: boolean;
38
+ parent_id?: string;
39
+ organisation_created_at?: string;
40
+ organisation_updated_at?: string;
30
41
  [key: string]: unknown;
31
42
  }
32
43
 
@@ -37,6 +48,7 @@ export class OrganisationService extends BaseService implements IOrganisationSer
37
48
  private _roleMapState: Map<string, string> = new Map();
38
49
  private _isLoading = false;
39
50
  private _error: Error | null = null;
51
+ private _isSuperAdmin: boolean = false; // Cache super admin status
40
52
  private _isContextReady = false;
41
53
  private retryCount = 0;
42
54
 
@@ -72,17 +84,21 @@ export class OrganisationService extends BaseService implements IOrganisationSer
72
84
  // Additional methods for testing
73
85
  setSelectedOrganisation(organisation: Organisation | null): void {
74
86
  // SECURITY: Validate organisation is in user's accessible organisations (only if orgs are loaded)
87
+ // Exception: Super admins can set any organisation (they have global access)
75
88
  if (organisation && this._organisations.length > 0) {
76
- const isValidOrg = this._organisations.some(org => org.id === organisation.id);
77
- if (!isValidOrg) {
78
- logger.warn('OrganisationService', 'Attempted to set invalid organisation - not in user\'s accessible organisations', {
79
- organisationId: organisation.id,
80
- organisationName: organisation.name,
81
- accessibleOrgIds: this._organisations.map(o => o.id)
82
- });
83
- // Don't set invalid organisation - this prevents security issues
84
- // If organisations haven't loaded yet, validation will happen in loadUserOrganisations()
85
- return;
89
+ // Only validate if user is not a super admin (use cached status)
90
+ if (!this._isSuperAdmin) {
91
+ const isValidOrg = this._organisations.some(org => org.id === organisation.id);
92
+ if (!isValidOrg) {
93
+ logger.warn('OrganisationService', 'Attempted to set invalid organisation - not in user\'s accessible organisations', {
94
+ organisationId: organisation.id,
95
+ organisationName: organisation.name,
96
+ accessibleOrgIds: this._organisations.map(o => o.id)
97
+ });
98
+ // Don't set invalid organisation - this prevents security issues
99
+ // If organisations haven't loaded yet, validation will happen in loadUserOrganisations()
100
+ return;
101
+ }
86
102
  }
87
103
  }
88
104
 
@@ -131,6 +147,11 @@ export class OrganisationService extends BaseService implements IOrganisationSer
131
147
  const wasAuthenticated = !!(this.user && this.session);
132
148
  const isAuthenticated = !!(user && session);
133
149
 
150
+ // Reset super admin cache when user changes
151
+ if (this.user?.id !== user?.id) {
152
+ this._isSuperAdmin = false;
153
+ }
154
+
134
155
  this.user = user;
135
156
  this.session = session;
136
157
 
@@ -358,159 +379,142 @@ export class OrganisationService extends BaseService implements IOrganisationSer
358
379
  this.notify();
359
380
 
360
381
  try {
361
- // Get user's organisation memberships using secure RPC function
362
- // Include all roles (org_admin, leader, member, supporter) - supporters can access PORTAL
363
- let memberships, membershipError;
382
+ // Get user's organisation roles directly from rbac_organisation_roles table
383
+ // This queries the source table directly instead of using the RPC which filters to match core_organisation_memberships view
384
+ // We still filter to active, non-revoked roles for the org selector
385
+ // The join includes organisation data, so we don't need a separate query that might be filtered by RLS
386
+ let memberships, membershipError, organisations: Organisation[] = [];
364
387
  try {
365
- // Add timeout and abort signal to prevent hanging RPC calls
366
- const timeoutPromise = new Promise((_, reject) => {
367
- const timeoutId = setTimeout(() => reject(new Error('RPC call timeout after 10 seconds')), 10000);
368
- abortSignal.addEventListener('abort', () => {
369
- clearTimeout(timeoutId);
370
- reject(new Error('Request aborted'));
371
- });
372
- });
373
-
374
- const rpcPromise = this.supabaseClient.rpc('data_user_organisation_roles_get', {
375
- p_user_id: this.user.id,
376
- p_organisation_id: null
377
- });
378
-
379
- // Check if request was aborted before making the call
388
+ // Check if request was aborted before making query
380
389
  if (abortSignal.aborted) {
381
390
  throw new Error('Request aborted');
382
391
  }
392
+
393
+ const { data: rolesData, error: rolesError } = await this.supabaseClient
394
+ .from('rbac_organisation_roles')
395
+ .select(`
396
+ id,
397
+ user_id,
398
+ organisation_id,
399
+ role,
400
+ status,
401
+ granted_at,
402
+ granted_by,
403
+ revoked_at,
404
+ revoked_by,
405
+ notes,
406
+ created_at,
407
+ updated_at,
408
+ core_organisations!inner(
409
+ id,
410
+ name,
411
+ display_name,
412
+ subscription_tier,
413
+ settings,
414
+ is_active,
415
+ parent_id,
416
+ created_at,
417
+ updated_at
418
+ )
419
+ `)
420
+ .eq('user_id', this.user.id)
421
+ .eq('status', 'active')
422
+ .is('revoked_at', null);
383
423
 
384
- const result = await Promise.race([rpcPromise, timeoutPromise]) as { data: OrganisationRoleRpcResponse[] | null; error: Error | null };
424
+ if (rolesError) {
425
+ logger.error("OrganisationService", "Error loading organisation roles:", rolesError);
426
+ throw rolesError;
427
+ }
385
428
 
386
- // Filter to organisation members (org_admin, leader, member, supporter)
387
- // Supporters are included to allow PORTAL access
388
- // Map to branded types when filtering
389
- memberships = result.data?.filter((role) =>
390
- ['org_admin', 'leader', 'member', 'supporter'].includes(role.role)
391
- ).map((m) => ({
429
+ // Map to branded types and extract organisation data from the join
430
+ // The join already includes organisation data, so we don't need a separate query
431
+ memberships = rolesData?.map((m) => ({
392
432
  ...m,
393
433
  user_id: assertUserId(m.user_id),
394
434
  organisation_id: assertOrganisationId(m.organisation_id),
395
435
  })) || [];
396
- membershipError = result.error;
397
- } catch (queryError) {
398
- membershipError = queryError instanceof Error ? queryError : new Error(String(queryError));
399
- }
400
-
401
- if (membershipError) {
402
- logger.error("OrganisationService", "Error loading memberships:", membershipError);
403
436
 
404
- // If RPC fails with timeout, try direct database query as fallback
405
- if (membershipError.message?.includes('timeout')) {
406
- try {
407
- // Check if request was aborted before making fallback query
408
- if (abortSignal.aborted) {
409
- throw new Error('Request aborted');
410
- }
411
-
412
- const { data: fallbackData, error: fallbackError } = await this.supabaseClient
413
- .from('rbac_organisation_roles')
414
- .select(`
415
- id,
416
- user_id,
417
- organisation_id,
418
- role,
419
- status,
420
- granted_at,
421
- granted_by,
422
- revoked_at,
423
- revoked_by,
424
- notes,
425
- created_at,
426
- updated_at,
427
- organisations!inner(
428
- id,
429
- name,
430
- display_name,
431
- subscription_tier,
432
- settings,
433
- is_active,
434
- parent_id,
435
- created_at,
436
- updated_at
437
- )
438
- `)
439
- .eq('user_id', this.user.id)
440
- .eq('status', 'active')
441
- .is('revoked_at', null)
442
- .in('role', ['org_admin', 'leader', 'member', 'supporter']);
443
-
444
- if (fallbackError) {
445
- logger.error("OrganisationService", "Fallback query also failed:", fallbackError);
446
- throw membershipError; // Throw original error
447
- }
448
-
449
- // Map to branded types
450
- memberships = fallbackData?.map((m) => ({
451
- ...m,
452
- user_id: assertUserId(m.user_id),
453
- organisation_id: assertOrganisationId(m.organisation_id),
454
- })) || [];
455
- membershipError = null;
456
- } catch (fallbackErr) {
457
- logger.error("OrganisationService", "Fallback query failed:", fallbackErr);
458
- throw membershipError; // Throw original error
437
+ // Extract unique organisations from the join results
438
+ // Use a Map to deduplicate by organisation ID
439
+ // Supabase returns joined data nested under the relation name
440
+ const organisationsMap = new Map<string, Organisation>();
441
+ rolesData?.forEach((role: any) => {
442
+ // The join returns organisation data nested under 'core_organisations' key
443
+ const orgData = role.core_organisations;
444
+ if (orgData && role.organisation_id && !organisationsMap.has(role.organisation_id)) {
445
+ organisationsMap.set(role.organisation_id, {
446
+ id: orgData.id,
447
+ name: orgData.name,
448
+ display_name: orgData.display_name,
449
+ subscription_tier: orgData.subscription_tier,
450
+ settings: orgData.settings,
451
+ is_active: orgData.is_active,
452
+ parent_id: orgData.parent_id,
453
+ created_at: orgData.created_at,
454
+ updated_at: orgData.updated_at,
455
+ } as Organisation);
459
456
  }
457
+ });
458
+
459
+ organisations = Array.from(organisationsMap.values());
460
+
461
+ // Extract organisations from join results
462
+ } catch (queryError) {
463
+ // Extract error message properly from Supabase error objects
464
+ if (queryError instanceof Error) {
465
+ membershipError = queryError;
466
+ } else if (queryError && typeof queryError === 'object' && 'message' in queryError) {
467
+ membershipError = new Error(String((queryError as any).message));
460
468
  } else {
461
- throw membershipError;
469
+ membershipError = new Error(String(queryError));
462
470
  }
471
+ logger.error("OrganisationService", "Error loading organisation roles:", membershipError);
472
+ throw membershipError;
463
473
  }
464
474
 
475
+ // Check if user is super admin - super admins don't need organisation memberships
476
+ let userIsSuperAdmin = false;
477
+ if (this.user?.id) {
478
+ try {
479
+ userIsSuperAdmin = await isSuperAdmin(this.user.id as UUID);
480
+ this._isSuperAdmin = userIsSuperAdmin; // Cache the result
481
+ } catch (error) {
482
+ logger.warn('OrganisationService', 'Failed to check super admin status', { error });
483
+ // Continue with normal flow if check fails
484
+ this._isSuperAdmin = false;
485
+ }
486
+ } else {
487
+ this._isSuperAdmin = false;
488
+ }
489
+
490
+ // Super admins can proceed without organisation memberships
465
491
  if (!memberships || memberships.length === 0) {
492
+ if (userIsSuperAdmin) {
493
+ // Super admin without org memberships - allow empty state
494
+ this._organisations = [];
495
+ this._userMemberships = [];
496
+ this._isLoading = false;
497
+ this._error = null;
498
+ this._isContextReady = true;
499
+ this.notify();
500
+ return;
501
+ }
466
502
  throw new Error('User has no active organisation memberships') as OrganisationSecurityError;
467
503
  }
468
504
 
469
- // Get organisation details for the memberships
470
- const organisationIds = memberships
471
- .map((m) => m.organisation_id)
472
- .filter((id: string) => {
473
- // Better validation to prevent empty string UUID errors
474
- if (!id || typeof id !== 'string') {
475
- logger.warn("OrganisationService", "Invalid organisation ID (not string):", id);
476
- return false;
477
- }
478
- const trimmedId = id.trim();
479
- if (trimmedId === '') {
480
- logger.warn("OrganisationService", "Empty organisation ID found");
481
- return false;
482
- }
483
- // Validate UUID format
484
- const isValidUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmedId);
485
- if (!isValidUuid) {
486
- logger.warn("OrganisationService", "Invalid UUID format:", trimmedId);
487
- }
488
- return isValidUuid;
489
- });
490
-
491
- if (organisationIds.length === 0) {
492
- logger.warn("OrganisationService", "No valid organisation IDs found in memberships:", memberships);
493
- throw new Error('No valid organisation IDs found in memberships') as OrganisationSecurityError;
494
- }
495
-
496
- // Check if request was aborted before making organisations query
497
- if (abortSignal.aborted) {
498
- throw new Error('Request aborted');
499
- }
500
-
501
- const { data: allOrganisations, error: orgError } = await this.supabaseClient
502
- .from('organisations')
503
- .select('id, name, display_name, subscription_tier, settings, is_active, parent_id, created_at, updated_at');
504
-
505
- if (orgError) {
506
- logger.error("OrganisationService", "Error loading organisations:", orgError);
507
- throw orgError;
505
+ if (!organisations || organisations.length === 0) {
506
+ if (userIsSuperAdmin) {
507
+ // Super admin without orgs - allow empty state
508
+ this._organisations = [];
509
+ this._userMemberships = [];
510
+ this._isLoading = false;
511
+ this._error = null;
512
+ this._isContextReady = true;
513
+ this.notify();
514
+ return;
515
+ }
516
+ throw new Error('No organisations found in role data') as OrganisationSecurityError;
508
517
  }
509
-
510
- // Filter manually on the client side
511
- const organisations = allOrganisations?.filter(org =>
512
- organisationIds.includes(org.id)
513
- ) || [];
514
518
 
515
519
  // Create a map of organisation_id to role from the memberships data
516
520
  const roleMap = new Map<string, string>();
@@ -522,7 +526,19 @@ export class OrganisationService extends BaseService implements IOrganisationSer
522
526
  const orgs = organisations as Organisation[];
523
527
  const activeOrgs = orgs.filter(org => org.is_active);
524
528
 
529
+ // Filter to active organisations only
530
+
525
531
  if (activeOrgs.length === 0) {
532
+ if (userIsSuperAdmin) {
533
+ // Super admin without active orgs - allow empty state
534
+ this._organisations = [];
535
+ this._userMemberships = [];
536
+ this._isLoading = false;
537
+ this._error = null;
538
+ this._isContextReady = true;
539
+ this.notify();
540
+ return;
541
+ }
526
542
  throw new Error('User has no access to active organisations') as OrganisationSecurityError;
527
543
  }
528
544
 
@@ -549,16 +565,13 @@ export class OrganisationService extends BaseService implements IOrganisationSer
549
565
  initialOrg = validPersistedOrg;
550
566
  selectionMethod = 'persisted';
551
567
  } else {
552
- logger.warn("OrganisationService", "Persisted organisation not found in active orgs, clearing cache");
553
568
  localStorage.removeItem('pace-core-selected-organisation');
554
569
  }
555
570
  } else {
556
- logger.warn("OrganisationService", "Invalid persisted organisation ID, clearing cache");
557
571
  localStorage.removeItem('pace-core-selected-organisation');
558
572
  }
559
573
  }
560
574
  } catch (storageError) {
561
- logger.warn("OrganisationService", "Failed to restore persisted organisation:", storageError);
562
575
  // Clear potentially corrupted cache
563
576
  localStorage.removeItem('pace-core-selected-organisation');
564
577
  }
@@ -610,10 +623,8 @@ export class OrganisationService extends BaseService implements IOrganisationSer
610
623
  } catch (err) {
611
624
  const error = err as Error;
612
625
  // "User has no access to active organisations" is a valid state for users without orgs (e.g., profile pages)
613
- // Log it as a warning instead of an error to reduce noise
614
- if (error.message === 'User has no access to active organisations') {
615
- logger.warn("OrganisationService", "User has no active organisations (this is expected for users without organisation access):", error);
616
- } else {
626
+ // Only log actual errors, not expected states
627
+ if (error.message !== 'User has no access to active organisations') {
617
628
  logger.error("OrganisationService", "Failed to load organisations:", err);
618
629
  }
619
630
  this._error = error;
@@ -88,16 +88,29 @@ describe('OrganisationService Pagination & Validation', () => {
88
88
  });
89
89
 
90
90
  mockSupabase.from.mockImplementation((table: string) => {
91
- if (table === 'organisations') {
91
+ if (table === 'rbac_organisation_roles') {
92
92
  return {
93
93
  select: vi.fn().mockResolvedValue({
94
- data: [mockOrganisation, mockOrganisation2],
94
+ data: [
95
+ {
96
+ ...mockMembership,
97
+ core_organisations: mockOrganisation
98
+ },
99
+ {
100
+ ...mockMembership2,
101
+ core_organisations: mockOrganisation2
102
+ }
103
+ ],
95
104
  error: null
96
- })
105
+ }),
106
+ eq: vi.fn().mockReturnThis(),
107
+ is: vi.fn().mockReturnThis()
97
108
  };
98
109
  }
99
110
  return {
100
- select: vi.fn().mockResolvedValue({ data: [], error: null })
111
+ select: vi.fn().mockResolvedValue({ data: [], error: null }),
112
+ eq: vi.fn().mockReturnThis(),
113
+ is: vi.fn().mockReturnThis()
101
114
  };
102
115
  });
103
116
 
@@ -175,16 +188,29 @@ describe('OrganisationService Pagination & Validation', () => {
175
188
  });
176
189
 
177
190
  mockSupabase.from.mockImplementation((table: string) => {
178
- if (table === 'organisations') {
191
+ if (table === 'rbac_organisation_roles') {
179
192
  return {
180
193
  select: vi.fn().mockResolvedValue({
181
- data: [mockOrganisation, inactiveOrganisation],
194
+ data: [
195
+ {
196
+ ...mockMembership,
197
+ core_organisations: mockOrganisation
198
+ },
199
+ {
200
+ ...inactiveMembership,
201
+ core_organisations: inactiveOrganisation
202
+ }
203
+ ],
182
204
  error: null
183
- })
205
+ }),
206
+ eq: vi.fn().mockReturnThis(),
207
+ is: vi.fn().mockReturnThis()
184
208
  };
185
209
  }
186
210
  return {
187
- select: vi.fn().mockResolvedValue({ data: [], error: null })
211
+ select: vi.fn().mockResolvedValue({ data: [], error: null }),
212
+ eq: vi.fn().mockReturnThis(),
213
+ is: vi.fn().mockReturnThis()
188
214
  };
189
215
  });
190
216