@jmruthers/pace-core 0.5.189 → 0.5.191

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 (438) hide show
  1. package/core-usage-manifest.json +0 -4
  2. package/dist/{AuthService-B-cd2MA4.d.ts → AuthService-CbP_utw2.d.ts} +7 -3
  3. package/dist/{DataTable-IVYljGJ6.d.ts → DataTable-Be6dH_dR.d.ts} +1 -1
  4. package/dist/{DataTable-GUFUNZ3N.js → DataTable-WKRZD47S.js} +8 -8
  5. package/dist/{PublicPageProvider-B8HaLe69.d.ts → PublicPageProvider-ULXC_u6U.d.ts} +84 -25
  6. package/dist/{UnifiedAuthProvider-BG0AL5eE.d.ts → UnifiedAuthProvider-BYA9qB-o.d.ts} +4 -3
  7. package/dist/{UnifiedAuthProvider-643PUAIM.js → UnifiedAuthProvider-FTSG5XH7.js} +4 -2
  8. package/dist/{api-YP7XD5L6.js → api-IHKALJZD.js} +4 -2
  9. package/dist/{chunk-VGZZXKBR.js → chunk-6LTQQAT6.js} +351 -157
  10. package/dist/chunk-6LTQQAT6.js.map +1 -0
  11. package/dist/{chunk-MX64ZF6I.js → chunk-6TQDD426.js} +15 -15
  12. package/dist/chunk-6TQDD426.js.map +1 -0
  13. package/dist/{chunk-YHCN776L.js → chunk-G37KK66H.js} +2 -75
  14. package/dist/chunk-G37KK66H.js.map +1 -0
  15. package/dist/{chunk-THRPYOFK.js → chunk-HW3OVDUF.js} +5 -5
  16. package/dist/chunk-HW3OVDUF.js.map +1 -0
  17. package/dist/{chunk-F2IMUDXZ.js → chunk-I7PSE6JW.js} +75 -2
  18. package/dist/chunk-I7PSE6JW.js.map +1 -0
  19. package/dist/{chunk-IM4QE42D.js → chunk-LOMZXPSN.js} +141 -326
  20. package/dist/chunk-LOMZXPSN.js.map +1 -0
  21. package/dist/chunk-OETXORNB.js +614 -0
  22. package/dist/chunk-OETXORNB.js.map +1 -0
  23. package/dist/{chunk-HESYZWZW.js → chunk-QWWZ5CAQ.js} +2 -2
  24. package/dist/{chunk-HEHYGYOX.js → chunk-ROXMHMY2.js} +403 -46
  25. package/dist/chunk-ROXMHMY2.js.map +1 -0
  26. package/dist/{chunk-2UUZZJFT.js → chunk-ULHIJK66.js} +228 -177
  27. package/dist/{chunk-2UUZZJFT.js.map → chunk-ULHIJK66.js.map} +1 -1
  28. package/dist/{chunk-YGPFYGA6.js → chunk-VKB2CO4Z.js} +838 -503
  29. package/dist/chunk-VKB2CO4Z.js.map +1 -0
  30. package/dist/{chunk-3GOZZZYH.js → chunk-VRGWKHDB.js} +238 -301
  31. package/dist/chunk-VRGWKHDB.js.map +1 -0
  32. package/dist/{chunk-UCQSRW7Z.js → chunk-XNYQOL3Z.js} +431 -384
  33. package/dist/chunk-XNYQOL3Z.js.map +1 -0
  34. package/dist/{chunk-DDM4CCYT.js → chunk-XYXSXPUK.js} +79 -59
  35. package/dist/chunk-XYXSXPUK.js.map +1 -0
  36. package/dist/{chunk-SAUPYVLF.js → chunk-ZSAAAMVR.js} +1 -1
  37. package/dist/chunk-ZSAAAMVR.js.map +1 -0
  38. package/dist/components.d.ts +5 -6
  39. package/dist/components.js +19 -19
  40. package/dist/components.js.map +1 -1
  41. package/dist/{database.generated-DI89OQeI.d.ts → database.generated-CzIvgcPu.d.ts} +165 -201
  42. package/dist/eslint-rules/pace-core-compliance.cjs +0 -2
  43. package/dist/{file-reference-D037xOFK.d.ts → file-reference-BavO2eQj.d.ts} +13 -10
  44. package/dist/hooks.d.ts +20 -15
  45. package/dist/hooks.js +14 -8
  46. package/dist/hooks.js.map +1 -1
  47. package/dist/index.d.ts +17 -15
  48. package/dist/index.js +86 -81
  49. package/dist/index.js.map +1 -1
  50. package/dist/providers.d.ts +3 -3
  51. package/dist/providers.js +3 -1
  52. package/dist/rbac/index.d.ts +77 -13
  53. package/dist/rbac/index.js +12 -9
  54. package/dist/{types-Bwgl--Xo.d.ts → types-CEpcvwwF.d.ts} +1 -1
  55. package/dist/types.d.ts +3 -3
  56. package/dist/types.js +1 -1
  57. package/dist/{usePublicRouteParams-CTDELQ7H.d.ts → usePublicRouteParams-TZe0gy-4.d.ts} +17 -10
  58. package/dist/utils.d.ts +8 -8
  59. package/dist/utils.js +16 -16
  60. package/docs/README.md +2 -2
  61. package/docs/api/classes/ColumnFactory.md +1 -1
  62. package/docs/api/classes/ErrorBoundary.md +1 -1
  63. package/docs/api/classes/InvalidScopeError.md +2 -2
  64. package/docs/api/classes/Logger.md +1 -1
  65. package/docs/api/classes/MissingUserContextError.md +2 -2
  66. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  67. package/docs/api/classes/PermissionDeniedError.md +1 -1
  68. package/docs/api/classes/RBACAuditManager.md +2 -2
  69. package/docs/api/classes/RBACCache.md +1 -1
  70. package/docs/api/classes/RBACEngine.md +5 -5
  71. package/docs/api/classes/RBACError.md +1 -1
  72. package/docs/api/classes/RBACNotInitializedError.md +2 -2
  73. package/docs/api/classes/SecureSupabaseClient.md +25 -20
  74. package/docs/api/classes/StorageUtils.md +7 -4
  75. package/docs/api/enums/FileCategory.md +1 -1
  76. package/docs/api/enums/LogLevel.md +1 -1
  77. package/docs/api/enums/RBACErrorCode.md +1 -1
  78. package/docs/api/enums/RPCFunction.md +1 -1
  79. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  80. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  81. package/docs/api/interfaces/AggregateConfig.md +1 -1
  82. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  83. package/docs/api/interfaces/AvatarProps.md +1 -1
  84. package/docs/api/interfaces/BadgeProps.md +1 -1
  85. package/docs/api/interfaces/ButtonProps.md +1 -1
  86. package/docs/api/interfaces/CalendarProps.md +20 -6
  87. package/docs/api/interfaces/CardProps.md +1 -1
  88. package/docs/api/interfaces/ColorPalette.md +1 -1
  89. package/docs/api/interfaces/ColorShade.md +1 -1
  90. package/docs/api/interfaces/ComplianceResult.md +1 -1
  91. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  92. package/docs/api/interfaces/DataRecord.md +1 -1
  93. package/docs/api/interfaces/DataTableAction.md +1 -1
  94. package/docs/api/interfaces/DataTableColumn.md +1 -1
  95. package/docs/api/interfaces/DataTableProps.md +1 -1
  96. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  97. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  98. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  99. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  100. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  101. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  102. package/docs/api/interfaces/ExportColumn.md +1 -1
  103. package/docs/api/interfaces/ExportOptions.md +1 -1
  104. package/docs/api/interfaces/FileDisplayProps.md +62 -16
  105. package/docs/api/interfaces/FileMetadata.md +1 -1
  106. package/docs/api/interfaces/FileReference.md +2 -2
  107. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  108. package/docs/api/interfaces/FileUploadOptions.md +26 -12
  109. package/docs/api/interfaces/FileUploadProps.md +30 -19
  110. package/docs/api/interfaces/FooterProps.md +1 -1
  111. package/docs/api/interfaces/FormFieldProps.md +1 -1
  112. package/docs/api/interfaces/FormProps.md +1 -1
  113. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  114. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  115. package/docs/api/interfaces/InputProps.md +1 -1
  116. package/docs/api/interfaces/LabelProps.md +1 -1
  117. package/docs/api/interfaces/LoggerConfig.md +1 -1
  118. package/docs/api/interfaces/LoginFormProps.md +1 -1
  119. package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
  120. package/docs/api/interfaces/NavigationContextType.md +9 -9
  121. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  122. package/docs/api/interfaces/NavigationItem.md +1 -1
  123. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  124. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  125. package/docs/api/interfaces/Organisation.md +1 -1
  126. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  127. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  128. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  129. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  130. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  131. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  132. package/docs/api/interfaces/PageAccessRecord.md +8 -8
  133. package/docs/api/interfaces/PagePermissionContextType.md +8 -8
  134. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  135. package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
  136. package/docs/api/interfaces/PaletteData.md +1 -1
  137. package/docs/api/interfaces/ParsedAddress.md +2 -2
  138. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  139. package/docs/api/interfaces/ProgressProps.md +3 -11
  140. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  141. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  142. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  143. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  144. package/docs/api/interfaces/QuickFix.md +1 -1
  145. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  146. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  147. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  148. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  149. package/docs/api/interfaces/RBACConfig.md +2 -2
  150. package/docs/api/interfaces/RBACContext.md +1 -1
  151. package/docs/api/interfaces/RBACLogger.md +1 -1
  152. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  153. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  154. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  155. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  156. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  157. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  158. package/docs/api/interfaces/RBACResult.md +1 -1
  159. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  160. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  161. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  162. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  163. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  164. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  165. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  166. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  167. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  168. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  169. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  170. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  171. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  172. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  173. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  174. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  175. package/docs/api/interfaces/RouteConfig.md +10 -10
  176. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  177. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  178. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  179. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  180. package/docs/api/interfaces/SetupIssue.md +1 -1
  181. package/docs/api/interfaces/StorageConfig.md +4 -4
  182. package/docs/api/interfaces/StorageFileInfo.md +7 -7
  183. package/docs/api/interfaces/StorageFileMetadata.md +25 -14
  184. package/docs/api/interfaces/StorageListOptions.md +22 -9
  185. package/docs/api/interfaces/StorageListResult.md +4 -4
  186. package/docs/api/interfaces/StorageUploadOptions.md +21 -8
  187. package/docs/api/interfaces/StorageUploadResult.md +6 -6
  188. package/docs/api/interfaces/StorageUrlOptions.md +19 -6
  189. package/docs/api/interfaces/StyleImport.md +1 -1
  190. package/docs/api/interfaces/SwitchProps.md +1 -1
  191. package/docs/api/interfaces/TabsContentProps.md +1 -1
  192. package/docs/api/interfaces/TabsListProps.md +1 -1
  193. package/docs/api/interfaces/TabsProps.md +1 -1
  194. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  195. package/docs/api/interfaces/TextareaProps.md +1 -1
  196. package/docs/api/interfaces/ToastActionElement.md +1 -1
  197. package/docs/api/interfaces/ToastProps.md +1 -1
  198. package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
  199. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  200. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  201. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  202. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  203. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  204. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  205. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  206. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  207. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  208. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  209. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  210. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  211. package/docs/api/interfaces/UseResolvedScopeOptions.md +5 -5
  212. package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
  213. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  214. package/docs/api/interfaces/UserEventAccess.md +11 -11
  215. package/docs/api/interfaces/UserMenuProps.md +1 -1
  216. package/docs/api/interfaces/UserProfile.md +1 -1
  217. package/docs/api/modules.md +165 -106
  218. package/docs/api-reference/components.md +15 -7
  219. package/docs/api-reference/providers.md +2 -2
  220. package/docs/api-reference/rpc-functions.md +1 -0
  221. package/docs/best-practices/README.md +1 -1
  222. package/docs/best-practices/deployment.md +8 -8
  223. package/docs/getting-started/examples/README.md +2 -2
  224. package/docs/getting-started/installation-guide.md +4 -4
  225. package/docs/getting-started/quick-start.md +3 -3
  226. package/docs/migration/MIGRATION_GUIDE.md +3 -3
  227. package/docs/migration/README.md +18 -0
  228. package/docs/migration/database-changes-december-2025.md +767 -0
  229. package/docs/migration/person-scoped-profiles-migration-guide.md +472 -0
  230. package/docs/rbac/compliance/compliance-guide.md +2 -2
  231. package/docs/rbac/event-based-apps.md +2 -2
  232. package/docs/rbac/getting-started.md +2 -2
  233. package/docs/rbac/quick-start.md +2 -2
  234. package/docs/security/README.md +4 -4
  235. package/docs/standards/07-rbac-and-rls-standard.md +430 -7
  236. package/docs/troubleshooting/README.md +2 -2
  237. package/docs/troubleshooting/migration.md +3 -3
  238. package/package.json +1 -3
  239. package/scripts/check-pace-core-compliance.cjs +1 -1
  240. package/scripts/check-pace-core-compliance.js +1 -1
  241. package/src/__tests__/fixtures/supabase.ts +301 -0
  242. package/src/__tests__/public-recipe-view.test.ts +19 -19
  243. package/src/__tests__/rls-policies.test.ts +210 -74
  244. package/src/components/AddressField/AddressField.test.tsx +42 -0
  245. package/src/components/AddressField/AddressField.tsx +71 -60
  246. package/src/components/AddressField/README.md +7 -6
  247. package/src/components/Alert/Alert.test.tsx +50 -10
  248. package/src/components/Alert/Alert.tsx +5 -3
  249. package/src/components/Avatar/Avatar.test.tsx +95 -43
  250. package/src/components/Avatar/Avatar.tsx +16 -16
  251. package/src/components/Button/Button.test.tsx +2 -1
  252. package/src/components/Button/Button.tsx +3 -3
  253. package/src/components/Calendar/Calendar.test.tsx +53 -37
  254. package/src/components/Calendar/Calendar.tsx +409 -82
  255. package/src/components/Card/Card.test.tsx +7 -4
  256. package/src/components/Card/Card.tsx +3 -6
  257. package/src/components/Checkbox/Checkbox.tsx +2 -2
  258. package/src/components/DataTable/components/ActionButtons.tsx +5 -5
  259. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  260. package/src/components/DataTable/components/ColumnFilter.tsx +1 -1
  261. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +3 -3
  262. package/src/components/DataTable/components/DataTableBody.tsx +12 -12
  263. package/src/components/DataTable/components/DataTableCore.tsx +3 -3
  264. package/src/components/DataTable/components/DataTableToolbar.tsx +5 -5
  265. package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -3
  266. package/src/components/DataTable/components/EditableRow.tsx +2 -2
  267. package/src/components/DataTable/components/EmptyState.tsx +3 -3
  268. package/src/components/DataTable/components/GroupHeader.tsx +2 -2
  269. package/src/components/DataTable/components/GroupingDropdown.tsx +1 -1
  270. package/src/components/DataTable/components/ImportModal.tsx +4 -4
  271. package/src/components/DataTable/components/LoadingState.tsx +1 -1
  272. package/src/components/DataTable/components/PaginationControls.tsx +11 -11
  273. package/src/components/DataTable/components/UnifiedTableBody.tsx +9 -9
  274. package/src/components/DataTable/components/ViewRowModal.tsx +2 -2
  275. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +11 -37
  276. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +157 -0
  277. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +2 -1
  278. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +128 -0
  279. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +19 -0
  280. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +51 -0
  281. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +84 -0
  282. package/src/components/DataTable/core/__tests__/DataManager.test.ts +14 -0
  283. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +136 -0
  284. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +16 -0
  285. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +18 -0
  286. package/src/components/DataTable/hooks/useDataTablePermissions.ts +28 -7
  287. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +30 -1
  288. package/src/components/DataTable/utils/hierarchicalUtils.ts +38 -10
  289. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -3
  290. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +4 -4
  291. package/src/components/Dialog/Dialog.tsx +2 -2
  292. package/src/components/EventSelector/EventSelector.tsx +7 -7
  293. package/src/components/FileDisplay/FileDisplay.tsx +291 -179
  294. package/src/components/FileUpload/FileUpload.tsx +7 -4
  295. package/src/components/Header/Header.test.tsx +28 -0
  296. package/src/components/Header/Header.tsx +22 -9
  297. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -2
  298. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +19 -14
  299. package/src/components/LoadingSpinner/LoadingSpinner.tsx +5 -5
  300. package/src/components/NavigationMenu/NavigationMenu.test.tsx +127 -1
  301. package/src/components/OrganisationSelector/OrganisationSelector.tsx +42 -22
  302. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +4 -0
  303. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +3 -0
  304. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +3 -0
  305. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +16 -6
  306. package/src/components/PaceAppLayout/PaceAppLayout.tsx +37 -3
  307. package/src/components/PaceAppLayout/test-setup.tsx +1 -0
  308. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +66 -45
  309. package/src/components/PaceLoginPage/PaceLoginPage.tsx +6 -4
  310. package/src/components/Progress/Progress.test.tsx +18 -19
  311. package/src/components/Progress/Progress.tsx +31 -32
  312. package/src/components/PublicLayout/PublicLayout.test.tsx +6 -6
  313. package/src/components/PublicLayout/PublicPageProvider.tsx +5 -3
  314. package/src/components/Select/Select.test.tsx +4 -1
  315. package/src/components/Select/Select.tsx +65 -20
  316. package/src/components/Switch/Switch.test.tsx +2 -1
  317. package/src/components/Switch/Switch.tsx +1 -1
  318. package/src/components/Toast/Toast.tsx +1 -1
  319. package/src/components/Tooltip/Tooltip.test.tsx +8 -2
  320. package/src/components/UserMenu/UserMenu.tsx +3 -3
  321. package/src/eslint-rules/pace-core-compliance.cjs +0 -2
  322. package/src/eslint-rules/pace-core-compliance.js +0 -2
  323. package/src/hooks/__tests__/hooks.integration.test.tsx +4 -1
  324. package/src/hooks/__tests__/useAppConfig.unit.test.ts +76 -5
  325. package/src/hooks/__tests__/useDataTableState.test.ts +76 -0
  326. package/src/hooks/__tests__/useFileUrl.unit.test.ts +25 -69
  327. package/src/hooks/__tests__/useFileUrlCache.test.ts +129 -0
  328. package/src/hooks/__tests__/usePreventTabReload.test.ts +88 -0
  329. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +1 -1
  330. package/src/hooks/__tests__/usePublicEvent.test.ts +608 -0
  331. package/src/hooks/__tests__/useQueryCache.test.ts +144 -0
  332. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +67 -24
  333. package/src/hooks/index.ts +1 -1
  334. package/src/hooks/public/usePublicEvent.ts +10 -10
  335. package/src/hooks/public/usePublicFileDisplay.ts +173 -87
  336. package/src/hooks/useAppConfig.ts +24 -5
  337. package/src/hooks/useFileDisplay.ts +298 -36
  338. package/src/hooks/useFileReference.ts +56 -11
  339. package/src/hooks/useFileUrl.ts +1 -1
  340. package/src/hooks/useInactivityTracker.ts +16 -7
  341. package/src/hooks/usePermissionCache.test.ts +85 -8
  342. package/src/hooks/useQueryCache.ts +27 -6
  343. package/src/hooks/useSecureDataAccess.test.ts +87 -42
  344. package/src/hooks/useSecureDataAccess.ts +95 -48
  345. package/src/providers/__tests__/OrganisationProvider.test.tsx +27 -21
  346. package/src/providers/services/EventServiceProvider.tsx +37 -17
  347. package/src/providers/services/InactivityServiceProvider.tsx +4 -4
  348. package/src/providers/services/OrganisationServiceProvider.tsx +8 -1
  349. package/src/providers/services/UnifiedAuthProvider.tsx +115 -29
  350. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +451 -0
  351. package/src/rbac/__tests__/engine.comprehensive.test.ts +12 -0
  352. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +8 -0
  353. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +4 -0
  354. package/src/rbac/api.ts +240 -36
  355. package/src/rbac/cache-invalidation.ts +21 -7
  356. package/src/rbac/compliance/quick-fix-suggestions.ts +1 -1
  357. package/src/rbac/components/NavigationGuard.tsx +23 -63
  358. package/src/rbac/components/NavigationProvider.test.tsx +52 -23
  359. package/src/rbac/components/NavigationProvider.tsx +13 -11
  360. package/src/rbac/components/PagePermissionGuard.tsx +77 -203
  361. package/src/rbac/components/PagePermissionProvider.tsx +13 -11
  362. package/src/rbac/components/PermissionEnforcer.tsx +24 -62
  363. package/src/rbac/components/RoleBasedRouter.tsx +14 -12
  364. package/src/rbac/components/SecureDataProvider.tsx +13 -11
  365. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +104 -41
  366. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +49 -12
  367. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +22 -1
  368. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +161 -82
  369. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +22 -1
  370. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +77 -30
  371. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +39 -5
  372. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +47 -4
  373. package/src/rbac/engine.ts +4 -2
  374. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +144 -52
  375. package/src/rbac/hooks/index.ts +3 -0
  376. package/src/rbac/hooks/useCan.test.ts +101 -53
  377. package/src/rbac/hooks/usePermissions.ts +108 -41
  378. package/src/rbac/hooks/useRBAC.test.ts +11 -3
  379. package/src/rbac/hooks/useRBAC.ts +83 -40
  380. package/src/rbac/hooks/useResolvedScope.test.ts +189 -63
  381. package/src/rbac/hooks/useResolvedScope.ts +128 -70
  382. package/src/rbac/hooks/useSecureSupabase.ts +36 -19
  383. package/src/rbac/hooks/useSuperAdminBypass.ts +126 -0
  384. package/src/rbac/request-deduplication.ts +1 -1
  385. package/src/rbac/secureClient.ts +72 -12
  386. package/src/rbac/security.ts +29 -23
  387. package/src/rbac/types.ts +10 -0
  388. package/src/rbac/utils/__tests__/contextValidator.test.ts +150 -0
  389. package/src/rbac/utils/__tests__/deep-equal.test.ts +53 -0
  390. package/src/rbac/utils/__tests__/eventContext.test.ts +8 -3
  391. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +74 -12
  392. package/src/rbac/utils/contextValidator.ts +288 -0
  393. package/src/rbac/utils/eventContext.ts +52 -3
  394. package/src/services/AuthService.ts +37 -8
  395. package/src/services/EventService.ts +165 -21
  396. package/src/services/OrganisationService.ts +125 -137
  397. package/src/services/__tests__/EventService.test.ts +26 -21
  398. package/src/services/__tests__/OrganisationService.pagination.test.ts +34 -8
  399. package/src/services/__tests__/OrganisationService.test.ts +218 -86
  400. package/src/types/database.generated.ts +166 -201
  401. package/src/types/file-reference.ts +13 -10
  402. package/src/types/supabase.ts +2 -2
  403. package/src/utils/__tests__/secureDataAccess.unit.test.ts +3 -2
  404. package/src/utils/app/appNameResolver.test.ts +346 -73
  405. package/src/utils/context/superAdminOverride.ts +58 -0
  406. package/src/utils/file-reference/index.ts +65 -37
  407. package/src/utils/google-places/googlePlacesUtils.test.ts +98 -0
  408. package/src/utils/google-places/googlePlacesUtils.ts +1 -1
  409. package/src/utils/google-places/loadGoogleMapsScript.test.ts +83 -0
  410. package/src/utils/google-places/types.ts +1 -1
  411. package/src/utils/request-deduplication.ts +4 -4
  412. package/src/utils/security/secureDataAccess.test.ts +1 -1
  413. package/src/utils/security/secureDataAccess.ts +7 -4
  414. package/src/utils/storage/README.md +1 -1
  415. package/src/utils/storage/helpers.test.ts +1 -1
  416. package/src/utils/storage/helpers.ts +38 -19
  417. package/src/utils/storage/types.ts +15 -8
  418. package/src/utils/validation/__tests__/csrf.test.ts +105 -0
  419. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +92 -0
  420. package/src/vite-env.d.ts +2 -2
  421. package/dist/chunk-3GOZZZYH.js.map +0 -1
  422. package/dist/chunk-DDM4CCYT.js.map +0 -1
  423. package/dist/chunk-E7UAOUMY.js +0 -75
  424. package/dist/chunk-E7UAOUMY.js.map +0 -1
  425. package/dist/chunk-F2IMUDXZ.js.map +0 -1
  426. package/dist/chunk-HEHYGYOX.js.map +0 -1
  427. package/dist/chunk-IM4QE42D.js.map +0 -1
  428. package/dist/chunk-MX64ZF6I.js.map +0 -1
  429. package/dist/chunk-SAUPYVLF.js.map +0 -1
  430. package/dist/chunk-THRPYOFK.js.map +0 -1
  431. package/dist/chunk-UCQSRW7Z.js.map +0 -1
  432. package/dist/chunk-VGZZXKBR.js.map +0 -1
  433. package/dist/chunk-YGPFYGA6.js.map +0 -1
  434. package/dist/chunk-YHCN776L.js.map +0 -1
  435. /package/dist/{DataTable-GUFUNZ3N.js.map → DataTable-WKRZD47S.js.map} +0 -0
  436. /package/dist/{UnifiedAuthProvider-643PUAIM.js.map → UnifiedAuthProvider-FTSG5XH7.js.map} +0 -0
  437. /package/dist/{api-YP7XD5L6.js.map → api-IHKALJZD.js.map} +0 -0
  438. /package/dist/{chunk-HESYZWZW.js.map → chunk-QWWZ5CAQ.js.map} +0 -0
@@ -15,7 +15,7 @@
15
15
  * const loadData = async () => {
16
16
  * try {
17
17
  * // Automatically includes organisation_id filter
18
- * const events = await secureQuery('event', '*', { is_visible: true });
18
+ * const events = await secureQuery('core_events', '*', { is_visible: true });
19
19
  * console.log('Organisation events:', events);
20
20
  * } catch (error) {
21
21
  * console.error('Failed to load data:', error);
@@ -25,7 +25,7 @@
25
25
  * const createEvent = async (eventData) => {
26
26
  * try {
27
27
  * // Automatically sets organisation_id
28
- * const newEvent = await secureInsert('event', eventData);
28
+ * const newEvent = await secureInsert('core_events', eventData);
29
29
  * console.log('Created event:', newEvent);
30
30
  * } catch (error) {
31
31
  * console.error('Failed to create event:', error);
@@ -59,6 +59,8 @@ import { setOrganisationContext } from '../utils/context/organisationContext';
59
59
  import { logger } from '../utils/core/logger';
60
60
  import type { Permission } from '../rbac/types';
61
61
  import type { OrganisationSecurityError } from '../types/organisation';
62
+ import { useSuperAdminBypass } from '../rbac/hooks/useSuperAdminBypass';
63
+ import { useResolvedScope } from '../rbac/hooks/useResolvedScope';
62
64
 
63
65
  export interface SecureDataAccessReturn {
64
66
  /** Execute a secure query with organisation filtering */
@@ -149,13 +151,21 @@ export interface DataAccessRecord {
149
151
  * - RPC calls include organisation_id parameter
150
152
  */
151
153
  export function useSecureDataAccess(): SecureDataAccessReturn {
152
- const { supabase, user, session } = useUnifiedAuth();
153
- const { ensureOrganisationContext } = useOrganisations();
154
+ const { supabase, user, session, selectedOrganisation, selectedEvent } = useUnifiedAuth();
154
155
 
155
156
  // Get selected event for event-scoped RPC calls
156
157
  // Use useContext directly to safely check if EventServiceProvider is available
157
158
  const eventServiceContext = useContext(EventServiceContext);
158
- const selectedEvent = eventServiceContext?.eventService?.getSelectedEvent() || null;
159
+ const eventFromContext = eventServiceContext?.eventService?.getSelectedEvent() || null;
160
+ const effectiveSelectedEvent = selectedEvent || eventFromContext;
161
+ const { isSuperAdmin } = useSuperAdminBypass();
162
+
163
+ // Use resolved scope to get organisationId (derived from event if needed)
164
+ const { resolvedScope } = useResolvedScope({
165
+ supabase,
166
+ selectedOrganisationId: selectedOrganisation?.id || null,
167
+ selectedEventId: effectiveSelectedEvent?.event_id || null
168
+ });
159
169
 
160
170
  const validateContext = useCallback((): void => {
161
171
  if (!supabase) {
@@ -165,23 +175,29 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
165
175
  throw new Error('User must be authenticated with valid session') as OrganisationSecurityError;
166
176
  }
167
177
 
168
- try {
169
- ensureOrganisationContext();
170
- } catch (error) {
178
+ if (isSuperAdmin) {
179
+ return;
180
+ }
181
+
182
+ if (!resolvedScope?.organisationId) {
171
183
  throw new Error('Organisation context is required for data access') as OrganisationSecurityError;
172
184
  }
173
- }, [supabase, user, session, ensureOrganisationContext]);
185
+ }, [supabase, user, session, resolvedScope, isSuperAdmin]);
174
186
 
175
187
  const getCurrentOrganisationId = useCallback((): string => {
188
+ if (isSuperAdmin) {
189
+ // For super admins, try to get org from resolved scope or selectedOrganisation
190
+ return resolvedScope?.organisationId || selectedOrganisation?.id || '';
191
+ }
192
+
176
193
  validateContext();
177
- const currentOrg = ensureOrganisationContext();
178
- return currentOrg.id;
179
- }, [validateContext, ensureOrganisationContext]);
194
+ return resolvedScope?.organisationId || '';
195
+ }, [validateContext, resolvedScope, selectedOrganisation, isSuperAdmin]);
180
196
 
181
197
  // Set organisation context in database session
182
- const setOrganisationContextInSession = useCallback(async (organisationId: string): Promise<void> => {
183
- if (!supabase) {
184
- throw new Error('No Supabase client available') as OrganisationSecurityError;
198
+ const setOrganisationContextInSession = useCallback(async (organisationId?: string): Promise<void> => {
199
+ if (!supabase || !organisationId) {
200
+ return;
185
201
  }
186
202
 
187
203
  await setOrganisationContext(supabase, organisationId);
@@ -199,7 +215,8 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
199
215
  } = {}
200
216
  ): Promise<T[]> => {
201
217
  validateContext();
202
- const organisationId = getCurrentOrganisationId();
218
+ const bypassOrganisationFilter = isSuperAdmin;
219
+ const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();
203
220
 
204
221
  // Set organisation context in database session
205
222
  await setOrganisationContextInSession(organisationId);
@@ -210,27 +227,45 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
210
227
  .select(columns);
211
228
 
212
229
  // Add organisation filter only if table has organisation_id column
230
+ // Defense in depth strategy:
231
+ // - RLS policies are the primary security layer (cannot be bypassed)
232
+ // - Application-level filtering adds an additional layer of protection
213
233
  const tablesWithOrganisation = [
214
- 'event', 'organisation_settings',
234
+ 'core_events', 'organisation_settings',
215
235
  'rbac_event_app_roles', 'rbac_organisation_roles',
216
236
  // SECURITY: Phase 2 additions - complete organisation table mapping
217
237
  'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',
218
238
  // SECURITY: Emergency additions for Phase 1 fixes
219
- 'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member',
239
+ 'cake_meal', 'cake_mealtype', 'core_person',
240
+ // NOTE: core_member, medi_profile, core_contact, core_consent, core_identification, core_qualification
241
+ // are now person-scoped (not organisation-scoped) - removed from this list
220
242
  // SECURITY: Phase 3A additions - medical and personal data
221
- 'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',
222
- 'pace_consent', 'pace_contact', 'pace_identification', 'pace_identification_type', 'pace_qualification',
243
+ // NOTE: medi_condition, medi_diet, medi_action_plan, medi_profile_versions are now person-scoped
244
+ // (via medi_profile) - removed from this list
245
+ // core_identification_type remains organisation-scoped (lookup table)
246
+ 'core_identification_type',
223
247
  'form_responses', 'form_response_values', 'forms',
224
248
  // SECURITY: Phase 3B additions - remaining critical tables
225
249
  'invoice', 'line_item', 'credit_balance', 'payment_method',
226
250
  'form_contexts', 'form_field_config', 'form_fields',
227
251
  'cake_delivery', 'cake_diettype', 'cake_diner', 'cake_dish', 'cake_item',
228
252
  'cake_logistics', 'cake_mealplan', 'cake_package', 'cake_recipe', 'cake_supplier',
229
- 'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions'
253
+ 'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions',
254
+ // rbac_user_profiles has organisation_id but uses conditional filtering
255
+ 'rbac_user_profiles'
230
256
  ];
231
257
 
232
- if (tablesWithOrganisation.includes(table)) {
233
- query = query.eq('organisation_id', organisationId);
258
+ // Apply organisation filtering based on table and super admin status
259
+ if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {
260
+ // For rbac_user_profiles: Super admins see all (no filter), non-super-admins get filtered (defense in depth)
261
+ // For other tables: Always apply filter
262
+ if (table === 'rbac_user_profiles' && isSuperAdmin) {
263
+ // Super admin: No org filter - RLS handles access control
264
+ // This allows super admins to see all users across all organisations
265
+ } else {
266
+ // Non-super-admin OR other tables: Apply org filter as defense in depth
267
+ query = query.eq('organisation_id', organisationId);
268
+ }
234
269
  }
235
270
 
236
271
  // Apply additional filters
@@ -272,23 +307,26 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
272
307
  recordDataAccess(table, 'read', true, `SELECT ${columns} FROM ${table}`, filters);
273
308
 
274
309
  return (data as T[]) || [];
275
- }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);
310
+ }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);
276
311
 
277
312
  const secureInsert = useCallback(async <T = any>(
278
313
  table: string,
279
314
  data: Record<string, any>
280
315
  ): Promise<T> => {
281
316
  validateContext();
282
- const organisationId = getCurrentOrganisationId();
317
+ const bypassOrganisationFilter = isSuperAdmin;
318
+ const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();
283
319
 
284
320
  // Set organisation context in database session
285
321
  await setOrganisationContextInSession(organisationId);
286
322
 
287
323
  // Ensure organisation_id is set
288
- const secureData = {
289
- ...data,
290
- organisation_id: organisationId
291
- };
324
+ const secureData = bypassOrganisationFilter
325
+ ? { ...data }
326
+ : {
327
+ ...data,
328
+ organisation_id: organisationId
329
+ };
292
330
 
293
331
  const { data: insertData, error } = await supabase!
294
332
  .from(table)
@@ -302,7 +340,7 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
302
340
  }
303
341
 
304
342
  return insertData as T;
305
- }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);
343
+ }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);
306
344
 
307
345
  const secureUpdate = useCallback(async <T = any>(
308
346
  table: string,
@@ -310,7 +348,8 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
310
348
  filters: Record<string, any>
311
349
  ): Promise<T[]> => {
312
350
  validateContext();
313
- const organisationId = getCurrentOrganisationId();
351
+ const bypassOrganisationFilter = isSuperAdmin;
352
+ const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();
314
353
 
315
354
  // Set organisation context in database session
316
355
  await setOrganisationContextInSession(organisationId);
@@ -325,15 +364,15 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
325
364
 
326
365
  // Add organisation filter only if table has organisation_id column
327
366
  const tablesWithOrganisation = [
328
- 'event', 'organisation_settings',
367
+ 'core_events', 'organisation_settings',
329
368
  'rbac_event_app_roles', 'rbac_organisation_roles',
330
369
  // SECURITY: Phase 2 additions - complete organisation table mapping
331
370
  'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',
332
371
  // SECURITY: Emergency additions for Phase 1 fixes
333
- 'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member'
372
+ 'cake_meal', 'cake_mealtype', 'core_person', 'core_member'
334
373
  ];
335
374
 
336
- if (tablesWithOrganisation.includes(table)) {
375
+ if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {
337
376
  query = query.eq('organisation_id', organisationId);
338
377
  }
339
378
 
@@ -352,14 +391,15 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
352
391
  }
353
392
 
354
393
  return (updateData as T[]) || [];
355
- }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);
394
+ }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);
356
395
 
357
396
  const secureDelete = useCallback(async (
358
397
  table: string,
359
398
  filters: Record<string, any>
360
399
  ): Promise<void> => {
361
400
  validateContext();
362
- const organisationId = getCurrentOrganisationId();
401
+ const bypassOrganisationFilter = isSuperAdmin;
402
+ const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();
363
403
 
364
404
  // Set organisation context in database session
365
405
  await setOrganisationContextInSession(organisationId);
@@ -371,15 +411,15 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
371
411
 
372
412
  // Add organisation filter only if table has organisation_id column
373
413
  const tablesWithOrganisation = [
374
- 'event', 'organisation_settings',
414
+ 'core_events', 'organisation_settings',
375
415
  'rbac_event_app_roles', 'rbac_organisation_roles',
376
416
  // SECURITY: Phase 2 additions - complete organisation table mapping
377
417
  'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',
378
418
  // SECURITY: Emergency additions for Phase 1 fixes
379
- 'cake_meal', 'cake_mealtype', 'pace_person', 'pace_member',
419
+ 'cake_meal', 'cake_mealtype', 'core_person', 'core_member',
380
420
  // SECURITY: Phase 3A additions - medical and personal data
381
421
  'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',
382
- 'pace_consent', 'pace_contact', 'pace_identification', 'pace_identification_type', 'pace_qualification',
422
+ 'core_consent', 'core_contact', 'core_identification', 'core_identification_type', 'core_qualification',
383
423
  'form_responses', 'form_response_values', 'forms',
384
424
  // SECURITY: Phase 3B additions - remaining critical tables
385
425
  'invoice', 'line_item', 'credit_balance', 'payment_method',
@@ -389,7 +429,7 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
389
429
  'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions'
390
430
  ];
391
431
 
392
- if (tablesWithOrganisation.includes(table)) {
432
+ if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {
393
433
  query = query.eq('organisation_id', organisationId);
394
434
  }
395
435
 
@@ -406,14 +446,15 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
406
446
  logger.error('useSecureDataAccess', 'Delete failed', { table, filters, error });
407
447
  throw error;
408
448
  }
409
- }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase]);
449
+ }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);
410
450
 
411
451
  const secureRpc = useCallback(async <T = any>(
412
452
  functionName: string,
413
453
  params: Record<string, any> = {}
414
454
  ): Promise<T> => {
415
455
  validateContext();
416
- const organisationId = getCurrentOrganisationId();
456
+ const bypassOrganisationFilter = isSuperAdmin;
457
+ const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();
417
458
 
418
459
  // Set organisation context in database session
419
460
  await setOrganisationContextInSession(organisationId);
@@ -477,8 +518,13 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
477
518
  secureParams.p_user_id = user.id;
478
519
  }
479
520
 
480
- // Add organisation_id parameter
481
- secureParams[paramName] = organisationId;
521
+ // Add organisation_id parameter when needed
522
+ if (!bypassOrganisationFilter && organisationId) {
523
+ secureParams[paramName] = organisationId;
524
+ } else if (organisationId && !(paramName in params)) {
525
+ // Default to the current organisation if caller didn't specify one
526
+ secureParams[paramName] = organisationId;
527
+ }
482
528
 
483
529
  // Add p_event_id if function needs it and event is selected
484
530
  // CRITICAL: This must be added AFTER organisation_id but BEFORE caller params
@@ -505,7 +551,7 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
505
551
  }
506
552
 
507
553
  return data as T;
508
- }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, selectedEvent?.event_id, user?.id]);
554
+ }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, selectedEvent?.event_id, user?.id, isSuperAdmin]);
509
555
 
510
556
  // NEW: Phase 1 - Enhanced Security Features
511
557
  const [dataAccessHistory, setDataAccessHistory] = useState<DataAccessRecord[]>([]);
@@ -570,12 +616,13 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
570
616
  filters?: Record<string, any>
571
617
  ) => {
572
618
  if (!isAuditLogEnabled || !user?.id) return;
573
-
619
+ const auditOrganisationId = getCurrentOrganisationId() || 'super-admin-bypass';
620
+
574
621
  const record: DataAccessRecord = {
575
622
  table,
576
623
  operation,
577
624
  userId: user.id,
578
- organisationId: getCurrentOrganisationId(),
625
+ organisationId: auditOrganisationId,
579
626
  allowed,
580
627
  timestamp: new Date().toISOString(),
581
628
  query,
@@ -592,7 +639,7 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
592
639
  table,
593
640
  operation,
594
641
  userId: user.id,
595
- organisationId: getCurrentOrganisationId(),
642
+ organisationId: auditOrganisationId,
596
643
  timestamp: new Date().toISOString()
597
644
  });
598
645
  }
@@ -40,24 +40,7 @@ const createMockSupabaseClient = () => {
40
40
  const orgId = '123e4567-e89b-12d3-a456-426614174000'; // Valid UUID format
41
41
  const userId = '123e4567-e89b-12d3-a456-426614174001'; // Valid UUID format
42
42
 
43
- return {
44
- rpc: vi.fn().mockResolvedValue({
45
- data: [
46
- {
47
- user_id: userId,
48
- organisation_id: orgId,
49
- role: 'org_admin',
50
- status: 'active',
51
- },
52
- ],
53
- error: null,
54
- }),
55
- from: vi.fn((table: string) => {
56
- if (table === 'organisations') {
57
- return {
58
- select: vi.fn().mockResolvedValue({
59
- data: [
60
- {
43
+ const mockOrganisation = {
61
44
  id: orgId,
62
45
  name: 'Test Organisation 1',
63
46
  display_name: 'Test Organisation 1',
@@ -67,11 +50,34 @@ const createMockSupabaseClient = () => {
67
50
  parent_id: null,
68
51
  created_at: '2023-01-01T00:00:00Z',
69
52
  updated_at: '2023-01-01T00:00:00Z',
70
- },
71
- ],
53
+ };
54
+
55
+ const mockQueryBuilder = {
56
+ select: vi.fn().mockReturnThis(),
57
+ eq: vi.fn().mockReturnThis(),
58
+ is: vi.fn().mockResolvedValue({
59
+ data: [{
60
+ id: 'membership-1',
61
+ user_id: userId,
62
+ organisation_id: orgId,
63
+ role: 'org_admin',
64
+ status: 'active',
65
+ granted_at: '2023-01-01T00:00:00Z',
66
+ revoked_at: null,
67
+ core_organisations: mockOrganisation
68
+ }],
69
+ error: null
70
+ })
71
+ };
72
+
73
+ return {
74
+ rpc: vi.fn().mockResolvedValue({
75
+ data: [],
72
76
  error: null,
73
77
  }),
74
- };
78
+ from: vi.fn((table: string) => {
79
+ if (table === 'rbac_organisation_roles') {
80
+ return mockQueryBuilder;
75
81
  }
76
82
  return {
77
83
  select: vi.fn().mockResolvedValue({ data: [], error: null }),
@@ -31,17 +31,18 @@ export interface EventServiceProviderProps {
31
31
  setSelectedEventId: (eventId: string | null) => void;
32
32
  }
33
33
 
34
- export function EventServiceProvider({
35
- children,
36
- supabaseClient,
37
- user,
38
- session,
34
+ export function EventServiceProvider({
35
+ children,
36
+ supabaseClient,
37
+ user,
38
+ session,
39
39
  appName,
40
40
  selectedOrganisation,
41
41
  setSelectedEventId
42
42
  }: EventServiceProviderProps) {
43
43
  // Create service instance once with useRef to avoid recreation on dependency changes
44
44
  const eventServiceRef = useRef<EventService | null>(null);
45
+ const initializingRef = useRef(false);
45
46
 
46
47
  if (!eventServiceRef.current) {
47
48
  eventServiceRef.current = new EventService(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId);
@@ -53,25 +54,44 @@ export function EventServiceProvider({
53
54
  // Note: eventService is a ref and never changes, so we don't include it in dependencies
54
55
  useEffect(() => {
55
56
  let isMounted = true;
56
-
57
+
58
+ if (initializingRef.current) {
59
+ return;
60
+ }
61
+
57
62
  const updateAndInitialize = async () => {
58
- // Update dependencies (now async to handle user change cleanup)
59
- await eventService.updateDependencies(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId);
60
-
61
- if (!isMounted) return;
62
-
63
- // Re-initialize service when dependencies change
64
- await eventService.initialize().catch(error => {
65
- if (isMounted) {
66
- logger.error('EventServiceProvider', 'Failed to initialize event service:', error);
67
- }
68
- });
63
+ initializingRef.current = true;
64
+
65
+ try {
66
+ logger.debug('EventServiceProvider', 'Updating dependencies and initializing', {
67
+ hasUser: !!user,
68
+ hasSession: !!session,
69
+ appName,
70
+ hasSelectedOrganisation: !!selectedOrganisation,
71
+ organisationId: selectedOrganisation?.id
72
+ });
73
+
74
+ // Update dependencies (now async to handle user change cleanup)
75
+ await eventService.updateDependencies(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId);
76
+
77
+ if (!isMounted) return;
78
+
79
+ // Re-initialize service when dependencies change
80
+ await eventService.initialize().catch(error => {
81
+ if (isMounted) {
82
+ logger.error('EventServiceProvider', 'Failed to initialize event service:', error);
83
+ }
84
+ });
85
+ } finally {
86
+ initializingRef.current = false;
87
+ }
69
88
  };
70
89
 
71
90
  updateAndInitialize();
72
91
 
73
92
  return () => {
74
93
  isMounted = false;
94
+ initializingRef.current = false;
75
95
  };
76
96
  // eslint-disable-next-line react-hooks/exhaustive-deps
77
97
  // eventService is a ref and never changes, so we exclude it from dependencies
@@ -25,8 +25,8 @@ export interface InactivityServiceProviderProps {
25
25
  supabaseClient: SupabaseClient;
26
26
  user: User | null;
27
27
  session: Session | null;
28
- idleTimeoutMs?: number;
29
- warnBeforeMs?: number;
28
+ idleTimeoutMs: number; // REQUIRED: No default - must be explicitly provided
29
+ warnBeforeMs: number; // REQUIRED: No default - must be explicitly provided
30
30
  onIdleLogout: (reason: 'inactivity') => void;
31
31
  }
32
32
 
@@ -35,8 +35,8 @@ export function InactivityServiceProvider({
35
35
  supabaseClient,
36
36
  user,
37
37
  session,
38
- idleTimeoutMs = 30 * 60 * 1000,
39
- warnBeforeMs = 60 * 1000,
38
+ idleTimeoutMs, // REQUIRED: No default - must be explicitly provided
39
+ warnBeforeMs, // REQUIRED: No default - must be explicitly provided
40
40
  onIdleLogout
41
41
  }: InactivityServiceProviderProps) {
42
42
  // Create service instance once with useRef to avoid recreation on dependency changes
@@ -52,7 +52,14 @@ export function OrganisationServiceProvider({
52
52
  organisationService.initialize()
53
53
  .catch(error => {
54
54
  if (isMounted) {
55
- logger.error('OrganisationServiceProvider', 'Failed to initialize organisation service:', error);
55
+ // "User has no access to active organisations" is a valid state for users without orgs (e.g., profile pages)
56
+ // Log it as a warning instead of an error to reduce noise
57
+ const errorMessage = error instanceof Error ? error.message : String(error);
58
+ if (errorMessage === 'User has no access to active organisations') {
59
+ logger.warn('OrganisationServiceProvider', 'User has no active organisations (this is expected for users without organisation access):', error);
60
+ } else {
61
+ logger.error('OrganisationServiceProvider', 'Failed to initialize organisation service:', error);
62
+ }
56
63
  }
57
64
  });
58
65