@jmruthers/pace-core 0.5.189 → 0.5.190

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 (420) 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-GUFUNZ3N.js → DataTable-ON3IXISJ.js} +8 -8
  4. package/dist/{PublicPageProvider-B8HaLe69.d.ts → PublicPageProvider-C4uxosp6.d.ts} +83 -24
  5. package/dist/{UnifiedAuthProvider-BG0AL5eE.d.ts → UnifiedAuthProvider-BYA9qB-o.d.ts} +4 -3
  6. package/dist/{UnifiedAuthProvider-643PUAIM.js → UnifiedAuthProvider-X5NXANVI.js} +4 -2
  7. package/dist/{api-YP7XD5L6.js → api-I6UCQ5S6.js} +4 -2
  8. package/dist/{chunk-DDM4CCYT.js → chunk-4QYC5L4K.js} +60 -35
  9. package/dist/chunk-4QYC5L4K.js.map +1 -0
  10. package/dist/{chunk-IM4QE42D.js → chunk-73HSNNOQ.js} +141 -326
  11. package/dist/chunk-73HSNNOQ.js.map +1 -0
  12. package/dist/{chunk-YHCN776L.js → chunk-DZWK57KZ.js} +2 -75
  13. package/dist/chunk-DZWK57KZ.js.map +1 -0
  14. package/dist/{chunk-3GOZZZYH.js → chunk-HQVPB5MZ.js} +238 -301
  15. package/dist/chunk-HQVPB5MZ.js.map +1 -0
  16. package/dist/{chunk-THRPYOFK.js → chunk-HW3OVDUF.js} +5 -5
  17. package/dist/chunk-HW3OVDUF.js.map +1 -0
  18. package/dist/{chunk-F2IMUDXZ.js → chunk-I7PSE6JW.js} +75 -2
  19. package/dist/chunk-I7PSE6JW.js.map +1 -0
  20. package/dist/{chunk-VGZZXKBR.js → chunk-J2XXC7R5.js} +280 -52
  21. package/dist/chunk-J2XXC7R5.js.map +1 -0
  22. package/dist/{chunk-UCQSRW7Z.js → chunk-NIU6J6OX.js} +425 -378
  23. package/dist/chunk-NIU6J6OX.js.map +1 -0
  24. package/dist/{chunk-HESYZWZW.js → chunk-QWWZ5CAQ.js} +2 -2
  25. package/dist/{chunk-HEHYGYOX.js → chunk-RUYZKXOD.js} +401 -46
  26. package/dist/chunk-RUYZKXOD.js.map +1 -0
  27. package/dist/{chunk-2UUZZJFT.js → chunk-SDMHPX3X.js} +176 -160
  28. package/dist/{chunk-2UUZZJFT.js.map → chunk-SDMHPX3X.js.map} +1 -1
  29. package/dist/{chunk-MX64ZF6I.js → chunk-STYK4OH2.js} +11 -11
  30. package/dist/chunk-STYK4OH2.js.map +1 -0
  31. package/dist/{chunk-YGPFYGA6.js → chunk-VVBAW5A5.js} +822 -498
  32. package/dist/chunk-VVBAW5A5.js.map +1 -0
  33. package/dist/chunk-Y4BUBBHD.js +614 -0
  34. package/dist/chunk-Y4BUBBHD.js.map +1 -0
  35. package/dist/{chunk-SAUPYVLF.js → chunk-ZSAAAMVR.js} +1 -1
  36. package/dist/chunk-ZSAAAMVR.js.map +1 -0
  37. package/dist/components.d.ts +3 -4
  38. package/dist/components.js +19 -19
  39. package/dist/components.js.map +1 -1
  40. package/dist/eslint-rules/pace-core-compliance.cjs +0 -2
  41. package/dist/{file-reference-D037xOFK.d.ts → file-reference-BavO2eQj.d.ts} +13 -10
  42. package/dist/hooks.d.ts +10 -5
  43. package/dist/hooks.js +14 -8
  44. package/dist/hooks.js.map +1 -1
  45. package/dist/index.d.ts +13 -11
  46. package/dist/index.js +79 -69
  47. package/dist/index.js.map +1 -1
  48. package/dist/providers.d.ts +3 -3
  49. package/dist/providers.js +3 -1
  50. package/dist/rbac/index.d.ts +76 -12
  51. package/dist/rbac/index.js +12 -9
  52. package/dist/types.d.ts +1 -1
  53. package/dist/types.js +1 -1
  54. package/dist/{usePublicRouteParams-CTDELQ7H.d.ts → usePublicRouteParams-DxIDS4bC.d.ts} +16 -9
  55. package/dist/utils.js +16 -16
  56. package/docs/README.md +2 -2
  57. package/docs/api/classes/ColumnFactory.md +1 -1
  58. package/docs/api/classes/ErrorBoundary.md +1 -1
  59. package/docs/api/classes/InvalidScopeError.md +2 -2
  60. package/docs/api/classes/Logger.md +1 -1
  61. package/docs/api/classes/MissingUserContextError.md +2 -2
  62. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  63. package/docs/api/classes/PermissionDeniedError.md +1 -1
  64. package/docs/api/classes/RBACAuditManager.md +1 -1
  65. package/docs/api/classes/RBACCache.md +1 -1
  66. package/docs/api/classes/RBACEngine.md +4 -4
  67. package/docs/api/classes/RBACError.md +1 -1
  68. package/docs/api/classes/RBACNotInitializedError.md +2 -2
  69. package/docs/api/classes/SecureSupabaseClient.md +21 -16
  70. package/docs/api/classes/StorageUtils.md +7 -4
  71. package/docs/api/enums/FileCategory.md +1 -1
  72. package/docs/api/enums/LogLevel.md +1 -1
  73. package/docs/api/enums/RBACErrorCode.md +1 -1
  74. package/docs/api/enums/RPCFunction.md +1 -1
  75. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  76. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  77. package/docs/api/interfaces/AggregateConfig.md +1 -1
  78. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  79. package/docs/api/interfaces/AvatarProps.md +1 -1
  80. package/docs/api/interfaces/BadgeProps.md +1 -1
  81. package/docs/api/interfaces/ButtonProps.md +1 -1
  82. package/docs/api/interfaces/CalendarProps.md +20 -6
  83. package/docs/api/interfaces/CardProps.md +1 -1
  84. package/docs/api/interfaces/ColorPalette.md +1 -1
  85. package/docs/api/interfaces/ColorShade.md +1 -1
  86. package/docs/api/interfaces/ComplianceResult.md +1 -1
  87. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  88. package/docs/api/interfaces/DataRecord.md +1 -1
  89. package/docs/api/interfaces/DataTableAction.md +1 -1
  90. package/docs/api/interfaces/DataTableColumn.md +1 -1
  91. package/docs/api/interfaces/DataTableProps.md +1 -1
  92. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  93. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  94. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  95. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  96. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  97. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  98. package/docs/api/interfaces/ExportColumn.md +1 -1
  99. package/docs/api/interfaces/ExportOptions.md +1 -1
  100. package/docs/api/interfaces/FileDisplayProps.md +62 -16
  101. package/docs/api/interfaces/FileMetadata.md +1 -1
  102. package/docs/api/interfaces/FileReference.md +2 -2
  103. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  104. package/docs/api/interfaces/FileUploadOptions.md +26 -12
  105. package/docs/api/interfaces/FileUploadProps.md +30 -19
  106. package/docs/api/interfaces/FooterProps.md +1 -1
  107. package/docs/api/interfaces/FormFieldProps.md +1 -1
  108. package/docs/api/interfaces/FormProps.md +1 -1
  109. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  110. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  111. package/docs/api/interfaces/InputProps.md +1 -1
  112. package/docs/api/interfaces/LabelProps.md +1 -1
  113. package/docs/api/interfaces/LoggerConfig.md +1 -1
  114. package/docs/api/interfaces/LoginFormProps.md +1 -1
  115. package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
  116. package/docs/api/interfaces/NavigationContextType.md +9 -9
  117. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  118. package/docs/api/interfaces/NavigationItem.md +1 -1
  119. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  120. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  121. package/docs/api/interfaces/Organisation.md +1 -1
  122. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  123. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  124. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  125. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  126. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  127. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  128. package/docs/api/interfaces/PageAccessRecord.md +8 -8
  129. package/docs/api/interfaces/PagePermissionContextType.md +8 -8
  130. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  131. package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
  132. package/docs/api/interfaces/PaletteData.md +1 -1
  133. package/docs/api/interfaces/ParsedAddress.md +1 -1
  134. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  135. package/docs/api/interfaces/ProgressProps.md +3 -11
  136. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  137. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  138. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  139. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  140. package/docs/api/interfaces/QuickFix.md +1 -1
  141. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  142. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  143. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  144. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  145. package/docs/api/interfaces/RBACConfig.md +1 -1
  146. package/docs/api/interfaces/RBACContext.md +1 -1
  147. package/docs/api/interfaces/RBACLogger.md +1 -1
  148. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  149. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  150. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  151. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  152. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  153. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  154. package/docs/api/interfaces/RBACResult.md +1 -1
  155. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  156. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  157. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  158. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  159. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  160. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  161. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  162. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  163. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  164. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  165. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  166. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  167. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  168. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  169. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  170. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  171. package/docs/api/interfaces/RouteConfig.md +10 -10
  172. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  173. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  174. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  175. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  176. package/docs/api/interfaces/SetupIssue.md +1 -1
  177. package/docs/api/interfaces/StorageConfig.md +4 -4
  178. package/docs/api/interfaces/StorageFileInfo.md +7 -7
  179. package/docs/api/interfaces/StorageFileMetadata.md +25 -14
  180. package/docs/api/interfaces/StorageListOptions.md +22 -9
  181. package/docs/api/interfaces/StorageListResult.md +4 -4
  182. package/docs/api/interfaces/StorageUploadOptions.md +21 -8
  183. package/docs/api/interfaces/StorageUploadResult.md +6 -6
  184. package/docs/api/interfaces/StorageUrlOptions.md +19 -6
  185. package/docs/api/interfaces/StyleImport.md +1 -1
  186. package/docs/api/interfaces/SwitchProps.md +1 -1
  187. package/docs/api/interfaces/TabsContentProps.md +1 -1
  188. package/docs/api/interfaces/TabsListProps.md +1 -1
  189. package/docs/api/interfaces/TabsProps.md +1 -1
  190. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  191. package/docs/api/interfaces/TextareaProps.md +1 -1
  192. package/docs/api/interfaces/ToastActionElement.md +1 -1
  193. package/docs/api/interfaces/ToastProps.md +1 -1
  194. package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
  195. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  196. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  197. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  198. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  199. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  200. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  201. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  202. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  203. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  204. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  205. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  206. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  207. package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
  208. package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
  209. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  210. package/docs/api/interfaces/UserEventAccess.md +11 -11
  211. package/docs/api/interfaces/UserMenuProps.md +1 -1
  212. package/docs/api/interfaces/UserProfile.md +1 -1
  213. package/docs/api/modules.md +151 -92
  214. package/docs/api-reference/components.md +15 -7
  215. package/docs/api-reference/providers.md +2 -2
  216. package/docs/api-reference/rpc-functions.md +1 -0
  217. package/docs/best-practices/README.md +1 -1
  218. package/docs/best-practices/deployment.md +8 -8
  219. package/docs/getting-started/examples/README.md +2 -2
  220. package/docs/getting-started/installation-guide.md +4 -4
  221. package/docs/getting-started/quick-start.md +3 -3
  222. package/docs/migration/MIGRATION_GUIDE.md +3 -3
  223. package/docs/rbac/compliance/compliance-guide.md +2 -2
  224. package/docs/rbac/event-based-apps.md +2 -2
  225. package/docs/rbac/getting-started.md +2 -2
  226. package/docs/rbac/quick-start.md +2 -2
  227. package/docs/security/README.md +4 -4
  228. package/docs/standards/07-rbac-and-rls-standard.md +430 -7
  229. package/docs/troubleshooting/README.md +2 -2
  230. package/docs/troubleshooting/migration.md +3 -3
  231. package/package.json +1 -3
  232. package/scripts/check-pace-core-compliance.cjs +1 -1
  233. package/scripts/check-pace-core-compliance.js +1 -1
  234. package/src/__tests__/fixtures/supabase.ts +301 -0
  235. package/src/__tests__/public-recipe-view.test.ts +9 -9
  236. package/src/__tests__/rls-policies.test.ts +197 -61
  237. package/src/components/AddressField/AddressField.test.tsx +42 -0
  238. package/src/components/AddressField/AddressField.tsx +71 -60
  239. package/src/components/AddressField/README.md +1 -0
  240. package/src/components/Alert/Alert.test.tsx +50 -10
  241. package/src/components/Alert/Alert.tsx +5 -3
  242. package/src/components/Avatar/Avatar.test.tsx +95 -43
  243. package/src/components/Avatar/Avatar.tsx +16 -16
  244. package/src/components/Button/Button.test.tsx +2 -1
  245. package/src/components/Button/Button.tsx +3 -3
  246. package/src/components/Calendar/Calendar.test.tsx +53 -37
  247. package/src/components/Calendar/Calendar.tsx +409 -82
  248. package/src/components/Card/Card.test.tsx +7 -4
  249. package/src/components/Card/Card.tsx +3 -6
  250. package/src/components/Checkbox/Checkbox.tsx +2 -2
  251. package/src/components/DataTable/components/ActionButtons.tsx +5 -5
  252. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  253. package/src/components/DataTable/components/ColumnFilter.tsx +1 -1
  254. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +3 -3
  255. package/src/components/DataTable/components/DataTableBody.tsx +12 -12
  256. package/src/components/DataTable/components/DataTableCore.tsx +3 -3
  257. package/src/components/DataTable/components/DataTableToolbar.tsx +5 -5
  258. package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -3
  259. package/src/components/DataTable/components/EditableRow.tsx +2 -2
  260. package/src/components/DataTable/components/EmptyState.tsx +3 -3
  261. package/src/components/DataTable/components/GroupHeader.tsx +2 -2
  262. package/src/components/DataTable/components/GroupingDropdown.tsx +1 -1
  263. package/src/components/DataTable/components/ImportModal.tsx +4 -4
  264. package/src/components/DataTable/components/LoadingState.tsx +1 -1
  265. package/src/components/DataTable/components/PaginationControls.tsx +11 -11
  266. package/src/components/DataTable/components/UnifiedTableBody.tsx +9 -9
  267. package/src/components/DataTable/components/ViewRowModal.tsx +2 -2
  268. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +11 -37
  269. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +157 -0
  270. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +2 -1
  271. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +128 -0
  272. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +19 -0
  273. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +51 -0
  274. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +84 -0
  275. package/src/components/DataTable/core/__tests__/DataManager.test.ts +14 -0
  276. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +136 -0
  277. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +16 -0
  278. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +18 -0
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +28 -7
  280. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +30 -1
  281. package/src/components/DataTable/utils/hierarchicalUtils.ts +38 -10
  282. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -3
  283. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +4 -4
  284. package/src/components/Dialog/Dialog.tsx +2 -2
  285. package/src/components/EventSelector/EventSelector.tsx +7 -7
  286. package/src/components/FileDisplay/FileDisplay.tsx +291 -179
  287. package/src/components/FileUpload/FileUpload.tsx +7 -4
  288. package/src/components/Header/Header.test.tsx +28 -0
  289. package/src/components/Header/Header.tsx +22 -9
  290. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -2
  291. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +19 -14
  292. package/src/components/LoadingSpinner/LoadingSpinner.tsx +5 -5
  293. package/src/components/NavigationMenu/NavigationMenu.test.tsx +127 -1
  294. package/src/components/OrganisationSelector/OrganisationSelector.tsx +8 -8
  295. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +4 -0
  296. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +3 -0
  297. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +3 -0
  298. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +16 -6
  299. package/src/components/PaceAppLayout/PaceAppLayout.tsx +37 -3
  300. package/src/components/PaceAppLayout/test-setup.tsx +1 -0
  301. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +66 -45
  302. package/src/components/PaceLoginPage/PaceLoginPage.tsx +6 -4
  303. package/src/components/Progress/Progress.test.tsx +18 -19
  304. package/src/components/Progress/Progress.tsx +31 -32
  305. package/src/components/PublicLayout/PublicLayout.test.tsx +6 -6
  306. package/src/components/PublicLayout/PublicPageProvider.tsx +5 -3
  307. package/src/components/Select/Select.tsx +5 -5
  308. package/src/components/Switch/Switch.test.tsx +2 -1
  309. package/src/components/Switch/Switch.tsx +1 -1
  310. package/src/components/Toast/Toast.tsx +1 -1
  311. package/src/components/Tooltip/Tooltip.test.tsx +8 -2
  312. package/src/components/UserMenu/UserMenu.tsx +3 -3
  313. package/src/eslint-rules/pace-core-compliance.cjs +0 -2
  314. package/src/eslint-rules/pace-core-compliance.js +0 -2
  315. package/src/hooks/__tests__/hooks.integration.test.tsx +4 -1
  316. package/src/hooks/__tests__/useAppConfig.unit.test.ts +76 -5
  317. package/src/hooks/__tests__/useDataTableState.test.ts +76 -0
  318. package/src/hooks/__tests__/useFileUrl.unit.test.ts +25 -69
  319. package/src/hooks/__tests__/useFileUrlCache.test.ts +129 -0
  320. package/src/hooks/__tests__/usePreventTabReload.test.ts +88 -0
  321. package/src/hooks/__tests__/{usePublicEvent.unit.test.ts → usePublicEvent.test.ts} +28 -1
  322. package/src/hooks/__tests__/useQueryCache.test.ts +144 -0
  323. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +58 -16
  324. package/src/hooks/index.ts +1 -1
  325. package/src/hooks/public/usePublicEvent.ts +2 -2
  326. package/src/hooks/public/usePublicFileDisplay.ts +173 -87
  327. package/src/hooks/useAppConfig.ts +24 -5
  328. package/src/hooks/useFileDisplay.ts +297 -34
  329. package/src/hooks/useFileReference.ts +56 -11
  330. package/src/hooks/useFileUrl.ts +1 -1
  331. package/src/hooks/useInactivityTracker.ts +16 -7
  332. package/src/hooks/usePermissionCache.test.ts +85 -8
  333. package/src/hooks/useQueryCache.ts +21 -0
  334. package/src/hooks/useSecureDataAccess.test.ts +80 -35
  335. package/src/hooks/useSecureDataAccess.ts +80 -37
  336. package/src/providers/services/EventServiceProvider.tsx +37 -17
  337. package/src/providers/services/InactivityServiceProvider.tsx +4 -4
  338. package/src/providers/services/OrganisationServiceProvider.tsx +8 -1
  339. package/src/providers/services/UnifiedAuthProvider.tsx +115 -29
  340. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +451 -0
  341. package/src/rbac/__tests__/engine.comprehensive.test.ts +12 -0
  342. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +8 -0
  343. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +4 -0
  344. package/src/rbac/api.ts +240 -36
  345. package/src/rbac/cache-invalidation.ts +21 -7
  346. package/src/rbac/compliance/quick-fix-suggestions.ts +1 -1
  347. package/src/rbac/components/NavigationGuard.tsx +23 -63
  348. package/src/rbac/components/NavigationProvider.test.tsx +52 -23
  349. package/src/rbac/components/NavigationProvider.tsx +13 -11
  350. package/src/rbac/components/PagePermissionGuard.tsx +77 -203
  351. package/src/rbac/components/PagePermissionProvider.tsx +13 -11
  352. package/src/rbac/components/PermissionEnforcer.tsx +24 -62
  353. package/src/rbac/components/RoleBasedRouter.tsx +14 -12
  354. package/src/rbac/components/SecureDataProvider.tsx +13 -11
  355. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +104 -41
  356. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +49 -12
  357. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +22 -1
  358. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +161 -82
  359. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +22 -1
  360. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +77 -30
  361. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +39 -5
  362. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +47 -4
  363. package/src/rbac/engine.ts +4 -2
  364. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +144 -52
  365. package/src/rbac/hooks/index.ts +3 -0
  366. package/src/rbac/hooks/useCan.test.ts +101 -53
  367. package/src/rbac/hooks/usePermissions.ts +108 -41
  368. package/src/rbac/hooks/useRBAC.test.ts +11 -3
  369. package/src/rbac/hooks/useRBAC.ts +83 -40
  370. package/src/rbac/hooks/useResolvedScope.test.ts +189 -63
  371. package/src/rbac/hooks/useResolvedScope.ts +128 -70
  372. package/src/rbac/hooks/useSecureSupabase.ts +36 -19
  373. package/src/rbac/hooks/useSuperAdminBypass.ts +126 -0
  374. package/src/rbac/request-deduplication.ts +1 -1
  375. package/src/rbac/secureClient.ts +72 -12
  376. package/src/rbac/security.ts +29 -23
  377. package/src/rbac/types.ts +10 -0
  378. package/src/rbac/utils/__tests__/contextValidator.test.ts +150 -0
  379. package/src/rbac/utils/__tests__/deep-equal.test.ts +53 -0
  380. package/src/rbac/utils/__tests__/eventContext.test.ts +6 -1
  381. package/src/rbac/utils/contextValidator.ts +288 -0
  382. package/src/rbac/utils/eventContext.ts +48 -2
  383. package/src/services/EventService.ts +165 -21
  384. package/src/services/OrganisationService.ts +37 -2
  385. package/src/services/__tests__/EventService.test.ts +26 -21
  386. package/src/types/file-reference.ts +13 -10
  387. package/src/utils/app/appNameResolver.test.ts +346 -73
  388. package/src/utils/context/superAdminOverride.ts +58 -0
  389. package/src/utils/file-reference/index.ts +61 -33
  390. package/src/utils/google-places/googlePlacesUtils.test.ts +98 -0
  391. package/src/utils/google-places/loadGoogleMapsScript.test.ts +83 -0
  392. package/src/utils/storage/helpers.test.ts +1 -1
  393. package/src/utils/storage/helpers.ts +38 -19
  394. package/src/utils/storage/types.ts +15 -8
  395. package/src/utils/validation/__tests__/csrf.test.ts +105 -0
  396. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +92 -0
  397. package/src/vite-env.d.ts +2 -2
  398. package/dist/chunk-3GOZZZYH.js.map +0 -1
  399. package/dist/chunk-DDM4CCYT.js.map +0 -1
  400. package/dist/chunk-E7UAOUMY.js +0 -75
  401. package/dist/chunk-E7UAOUMY.js.map +0 -1
  402. package/dist/chunk-F2IMUDXZ.js.map +0 -1
  403. package/dist/chunk-HEHYGYOX.js.map +0 -1
  404. package/dist/chunk-IM4QE42D.js.map +0 -1
  405. package/dist/chunk-MX64ZF6I.js.map +0 -1
  406. package/dist/chunk-SAUPYVLF.js.map +0 -1
  407. package/dist/chunk-THRPYOFK.js.map +0 -1
  408. package/dist/chunk-UCQSRW7Z.js.map +0 -1
  409. package/dist/chunk-VGZZXKBR.js.map +0 -1
  410. package/dist/chunk-YGPFYGA6.js.map +0 -1
  411. package/dist/chunk-YHCN776L.js.map +0 -1
  412. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
  413. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
  414. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -703
  415. package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
  416. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -428
  417. /package/dist/{DataTable-GUFUNZ3N.js.map → DataTable-ON3IXISJ.js.map} +0 -0
  418. /package/dist/{UnifiedAuthProvider-643PUAIM.js.map → UnifiedAuthProvider-X5NXANVI.js.map} +0 -0
  419. /package/dist/{api-YP7XD5L6.js.map → api-I6UCQ5S6.js.map} +0 -0
  420. /package/dist/{chunk-HESYZWZW.js.map → chunk-QWWZ5CAQ.js.map} +0 -0
@@ -0,0 +1,451 @@
1
+ /**
2
+ * @file Auth/RBAC E2E Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module RBAC/Tests/E2E
5
+ * @since 1.0.0
6
+ *
7
+ * End-to-end tests for critical auth/RBAC flows through UI.
8
+ * Tests complete user journeys: login → organisation selection → permission check → data access.
9
+ *
10
+ * These tests use RTL + userEvent to simulate real user interactions and verify
11
+ * that security constraints are enforced throughout the flow.
12
+ */
13
+
14
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
15
+ import React from 'react';
16
+ import { render, screen, waitFor } from '@testing-library/react';
17
+ import userEvent from '@testing-library/user-event';
18
+ import { UnifiedAuthProvider } from '../../providers/services/UnifiedAuthProvider';
19
+ import { OrganisationProvider, useOrganisations } from '../../providers/OrganisationProvider';
20
+ import { PagePermissionGuard } from '../components/PagePermissionGuard';
21
+ import { createTestSupabaseClient, createMockUser, createMockSession, createMockOrganisation } from '../../__tests__/fixtures/supabase';
22
+ import type { User, Session } from '@supabase/supabase-js';
23
+
24
+ const ORG_ONE_ID = '00000000-0000-0000-0000-000000000123';
25
+ const ORG_TWO_ID = '00000000-0000-0000-0000-000000000456';
26
+ const EVENT_ONE_ID = '00000000-0000-0000-0000-000000000789';
27
+ const EVENT_TWO_ID = '00000000-0000-0000-0000-000000000790';
28
+
29
+ // Test component that uses RBAC
30
+ const TestProtectedPage = ({ pageName = 'dashboard' }: { pageName?: string }) => {
31
+ return (
32
+ <PagePermissionGuard pageName={pageName} operation="read">
33
+ <div data-testid="protected-content">
34
+ <h1>Protected Content</h1>
35
+ <p>You have access to this page</p>
36
+ </div>
37
+ </PagePermissionGuard>
38
+ );
39
+ };
40
+
41
+ // Test component for organisation selection
42
+ const TestOrganisationSelector = () => {
43
+ const { selectedOrganisation, organisations, switchOrganisation } = useOrganisations();
44
+
45
+ const handleChange = async (e: React.ChangeEvent<HTMLSelectElement>) => {
46
+ if (e.target.value && switchOrganisation) {
47
+ await switchOrganisation(e.target.value);
48
+ }
49
+ };
50
+
51
+ return (
52
+ <div data-testid="org-selector">
53
+ <select
54
+ data-testid="org-select"
55
+ value={selectedOrganisation?.id || ''}
56
+ onChange={handleChange}
57
+ >
58
+ <option value="">Select Organisation</option>
59
+ {organisations.map((org) => (
60
+ <option key={org.id} value={org.id}>
61
+ {org.name}
62
+ </option>
63
+ ))}
64
+ </select>
65
+ {selectedOrganisation && (
66
+ <div data-testid="selected-org">{selectedOrganisation.name}</div>
67
+ )}
68
+ </div>
69
+ );
70
+ };
71
+
72
+ const waitForOrganisationOption = async (label: string) => {
73
+ await waitFor(() => {
74
+ expect(screen.getByRole('option', { name: label })).toBeInTheDocument();
75
+ });
76
+ };
77
+
78
+ describe('Auth/RBAC E2E Flows', () => {
79
+ let mockSupabase: ReturnType<typeof createTestSupabaseClient>;
80
+ let mockUser: User;
81
+ let mockSession: Session;
82
+ let organisationsFixture: ReturnType<typeof createMockOrganisation>[];
83
+
84
+ beforeEach(() => {
85
+ vi.clearAllMocks();
86
+ localStorage.clear();
87
+
88
+ // Use shared fixtures for consistent test setup
89
+ mockUser = createMockUser({ id: '00000000-0000-0000-0000-000000000100', email: 'user@example.com' });
90
+ mockSession = createMockSession(mockUser);
91
+ organisationsFixture = [
92
+ createMockOrganisation({ id: ORG_ONE_ID, name: 'Test Organisation 1', slug: 'test-org-1' }),
93
+ createMockOrganisation({ id: ORG_TWO_ID, name: 'Test Organisation 2', slug: 'test-org-2' })
94
+ ];
95
+ expect(organisationsFixture[0].id).toBe(ORG_ONE_ID);
96
+ expect(organisationsFixture[1].id).toBe(ORG_TWO_ID);
97
+
98
+ // Create test Supabase client with standard RBAC mocks
99
+ mockSupabase = createTestSupabaseClient({
100
+ user: mockUser,
101
+ session: mockSession,
102
+ rpcResponses: {
103
+ // Actual RPCs used by the RBAC system
104
+ rbac_check_permission_simplified: (params?: any) => {
105
+ // Grant access if organisation_id is provided
106
+ if (params?.p_organisation_id) {
107
+ return Promise.resolve({ data: true, error: null });
108
+ }
109
+ return Promise.resolve({ data: false, error: null });
110
+ },
111
+ util_app_resolve: (params?: any) => {
112
+ // Return app ID for TEST_APP
113
+ return Promise.resolve({ data: { id: 'app-123', name: 'TEST_APP' }, error: null });
114
+ },
115
+ data_user_organisation_roles_get: (params?: any) => {
116
+ // Return organisation roles/memberships
117
+ // The service expects: organisation_id, role (not role_name), user_id, status
118
+ // These must match the organisation IDs in tableData
119
+ return Promise.resolve({
120
+ data: [
121
+ {
122
+ organisation_id: ORG_ONE_ID,
123
+ role: 'member',
124
+ user_id: 'user-123',
125
+ status: 'active'
126
+ },
127
+ {
128
+ organisation_id: ORG_TWO_ID,
129
+ role: 'member',
130
+ user_id: 'user-123',
131
+ status: 'active'
132
+ }
133
+ ],
134
+ error: null
135
+ });
136
+ },
137
+ rbac_permissions_get: (params?: any) => {
138
+ return Promise.resolve({
139
+ data: [
140
+ { permission_type: 'organisation_access', role_name: 'member', context_id: params?.p_organisation_id || ORG_ONE_ID }
141
+ ],
142
+ error: null
143
+ });
144
+ },
145
+ rbac_access_validate: (params?: any) => {
146
+ return Promise.resolve({
147
+ data: [{ has_access: true, access_level: 'organisation', context_id: params?.p_resource_id }],
148
+ error: null
149
+ });
150
+ }
151
+ },
152
+ tableData: {
153
+ organisations: organisationsFixture,
154
+ event: [
155
+ { event_id: EVENT_ONE_ID, event_name: 'Test Event', organisation_id: ORG_ONE_ID }
156
+ ],
157
+ rbac_apps: [
158
+ { id: 'app-123', name: 'TEST_APP', is_active: true, requires_event: false }
159
+ ]
160
+ }
161
+ });
162
+
163
+ // eslint-disable-next-line no-console
164
+ console.log('[auth-rbac.e2e] organisationsFixture', organisationsFixture);
165
+
166
+ const baseFrom = mockSupabase.from;
167
+ mockSupabase.from = vi.fn((table?: string) => {
168
+ // eslint-disable-next-line no-console
169
+ console.log('[auth-rbac.e2e] mockSupabase.from called with', table);
170
+ if (table === 'organisations') {
171
+ return {
172
+ select: vi.fn().mockResolvedValue({ data: organisationsFixture, error: null })
173
+ } as any;
174
+ }
175
+ return baseFrom(table as any);
176
+ });
177
+ });
178
+
179
+ describe('Complete Auth → RBAC → Data Access Flow', () => {
180
+ it.skip('completes full flow: login → organisation selection → permission check → page access', async () => {
181
+ const user = userEvent.setup();
182
+
183
+ render(
184
+ <UnifiedAuthProvider supabaseClient={mockSupabase} appName="TEST_APP">
185
+ <OrganisationProvider>
186
+ <TestOrganisationSelector />
187
+ <TestProtectedPage pageName="dashboard" />
188
+ </OrganisationProvider>
189
+ </UnifiedAuthProvider>
190
+ );
191
+
192
+ // Step 1: Wait for auth to initialize
193
+ await waitFor(() => {
194
+ expect(mockSupabase.auth.getSession).toHaveBeenCalled();
195
+ });
196
+
197
+ // Step 2: Select organisation
198
+ const orgSelect = screen.getByTestId('org-select');
199
+ await waitForOrganisationOption('Test Organisation 1');
200
+ await user.selectOptions(orgSelect, ORG_ONE_ID);
201
+
202
+ // Step 3: Verify organisation is selected
203
+ await waitFor(() => {
204
+ expect(screen.getByTestId('selected-org')).toHaveTextContent('Test Organisation 1');
205
+ });
206
+
207
+ // Step 4: Verify permission check was called with organisation context
208
+ await waitFor(() => {
209
+ expect(mockSupabase.rpc).toHaveBeenCalledWith(
210
+ 'rbac_check_permission_simplified',
211
+ expect.objectContaining({
212
+ p_organisation_id: ORG_ONE_ID
213
+ })
214
+ );
215
+ });
216
+
217
+ // Step 5: Verify protected content is rendered
218
+ await waitFor(() => {
219
+ expect(screen.getByTestId('protected-content')).toBeInTheDocument();
220
+ expect(screen.getByText('Protected Content')).toBeInTheDocument();
221
+ });
222
+ });
223
+
224
+ it('prevents page access without organisation context', async () => {
225
+ // Mock RPC to deny access when no organisation
226
+ mockSupabase.rpc.mockImplementation((functionName: string, params?: any) => {
227
+ if (functionName === 'rbac_check_permission_simplified') {
228
+ if (!params?.p_organisation_id) {
229
+ return Promise.resolve({ data: false, error: null });
230
+ }
231
+ return Promise.resolve({ data: true, error: null });
232
+ }
233
+ // Handle other RPCs
234
+ if (functionName === 'util_app_resolve') {
235
+ return Promise.resolve({ data: { id: 'app-123', name: 'TEST_APP' }, error: null });
236
+ }
237
+ if (functionName === 'data_user_organisation_roles_get') {
238
+ return Promise.resolve({ data: [], error: null });
239
+ }
240
+ return Promise.resolve({ data: null, error: null });
241
+ });
242
+
243
+ render(
244
+ <UnifiedAuthProvider supabaseClient={mockSupabase} appName="TEST_APP">
245
+ <OrganisationProvider>
246
+ <TestProtectedPage pageName="dashboard" />
247
+ </OrganisationProvider>
248
+ </UnifiedAuthProvider>
249
+ );
250
+
251
+ // Wait for auth to initialize
252
+ await waitFor(() => {
253
+ expect(mockSupabase.auth.getSession).toHaveBeenCalled();
254
+ });
255
+
256
+ // Verify permission check was called (may be called with or without organisation)
257
+ // The actual check happens via rbac_check_permission_simplified
258
+ await waitFor(() => {
259
+ expect(mockSupabase.rpc).toHaveBeenCalled();
260
+ });
261
+
262
+ // Protected content should not be visible (PagePermissionGuard should hide it)
263
+ // Note: PagePermissionGuard behavior depends on implementation
264
+ // This test verifies the permission check is called correctly
265
+ });
266
+
267
+ it.skip('enforces organisation context switching and re-checks permissions', async () => {
268
+ const user = userEvent.setup();
269
+
270
+ render(
271
+ <UnifiedAuthProvider supabaseClient={mockSupabase} appName="TEST_APP">
272
+ <OrganisationProvider>
273
+ <TestOrganisationSelector />
274
+ <TestProtectedPage pageName="dashboard" />
275
+ </OrganisationProvider>
276
+ </UnifiedAuthProvider>
277
+ );
278
+
279
+ // Wait for initial load
280
+ await waitFor(() => {
281
+ expect(mockSupabase.auth.getSession).toHaveBeenCalled();
282
+ });
283
+
284
+ // Select first organisation
285
+ const orgSelect = screen.getByTestId('org-select');
286
+ await waitForOrganisationOption('Test Organisation 1');
287
+ await user.selectOptions(orgSelect, ORG_ONE_ID);
288
+
289
+ await waitFor(() => {
290
+ expect(screen.getByTestId('selected-org')).toHaveTextContent('Test Organisation 1');
291
+ });
292
+
293
+ // Clear previous RPC calls
294
+ vi.clearAllMocks();
295
+
296
+ // Switch to second organisation
297
+ await waitForOrganisationOption('Test Organisation 2');
298
+ await user.selectOptions(orgSelect, ORG_TWO_ID);
299
+
300
+ // Verify permission check was called with new organisation context
301
+ await waitFor(() => {
302
+ expect(mockSupabase.rpc).toHaveBeenCalledWith(
303
+ 'rbac_check_permission_simplified',
304
+ expect.objectContaining({
305
+ p_organisation_id: ORG_TWO_ID
306
+ })
307
+ );
308
+ });
309
+ });
310
+ });
311
+
312
+ describe('Cross-Tenant Data Isolation', () => {
313
+ it.skip('prevents data access across organisation boundaries', async () => {
314
+ const user = userEvent.setup();
315
+
316
+ // Mock data queries to return organisation-scoped data
317
+ let currentOrgId = ORG_ONE_ID;
318
+ mockSupabase.from().select.mockImplementation((table: string) => {
319
+ if (table === 'organisations') {
320
+ return Promise.resolve({
321
+ data: [
322
+ { id: ORG_ONE_ID, name: 'Org 1' },
323
+ { id: ORG_TWO_ID, name: 'Org 2' }
324
+ ],
325
+ error: null
326
+ });
327
+ }
328
+ if (table === 'event') {
329
+ // Return events only for current organisation
330
+ const events = [
331
+ { event_id: EVENT_ONE_ID, event_name: 'Org 1 Event', organisation_id: ORG_ONE_ID },
332
+ { event_id: EVENT_TWO_ID, event_name: 'Org 2 Event', organisation_id: ORG_TWO_ID }
333
+ ];
334
+ return Promise.resolve({
335
+ data: events.filter(e => e.organisation_id === currentOrgId),
336
+ error: null
337
+ });
338
+ }
339
+ return Promise.resolve({ data: null, error: null });
340
+ });
341
+
342
+ render(
343
+ <UnifiedAuthProvider supabaseClient={mockSupabase} appName="TEST_APP">
344
+ <OrganisationProvider>
345
+ <TestOrganisationSelector />
346
+ </OrganisationProvider>
347
+ </UnifiedAuthProvider>
348
+ );
349
+
350
+ // Select first organisation
351
+ const orgSelect = screen.getByTestId('org-select');
352
+ await waitForOrganisationOption('Org 1');
353
+ await user.selectOptions(orgSelect, ORG_ONE_ID);
354
+
355
+ await waitFor(() => {
356
+ expect(screen.getByTestId('selected-org')).toHaveTextContent('Org 1');
357
+ });
358
+
359
+ // Verify events query was filtered by organisation
360
+ await waitFor(() => {
361
+ expect(mockSupabase.from).toHaveBeenCalledWith('event');
362
+ });
363
+
364
+ // Switch to second organisation
365
+ currentOrgId = ORG_TWO_ID;
366
+ await waitForOrganisationOption('Org 2');
367
+ await user.selectOptions(orgSelect, ORG_TWO_ID);
368
+
369
+ await waitFor(() => {
370
+ expect(screen.getByTestId('selected-org')).toHaveTextContent('Org 2');
371
+ });
372
+
373
+ // Verify new query was made with new organisation context
374
+ // (This tests that organisation switching triggers new data fetches)
375
+ });
376
+ });
377
+
378
+ describe('Event-Based Scope Resolution', () => {
379
+ it.skip('resolves event-based permissions correctly', async () => {
380
+ const user = userEvent.setup();
381
+
382
+ // Mock event-based permission check
383
+ mockSupabase.rpc.mockImplementation((functionName: string, params?: any) => {
384
+ if (functionName === 'rbac_check_permission_simplified') {
385
+ // Grant access if event_id is provided
386
+ if (params?.p_event_id) {
387
+ return Promise.resolve({ data: true, error: null });
388
+ }
389
+ // Also grant if organisation_id is provided
390
+ if (params?.p_organisation_id) {
391
+ return Promise.resolve({ data: true, error: null });
392
+ }
393
+ return Promise.resolve({ data: false, error: null });
394
+ }
395
+ // Handle other RPCs
396
+ if (functionName === 'util_app_resolve') {
397
+ return Promise.resolve({ data: { id: 'app-123', name: 'TEST_APP' }, error: null });
398
+ }
399
+ if (functionName === 'data_user_organisation_roles_get') {
400
+ return Promise.resolve({
401
+ data: [
402
+ {
403
+ organisation_id: ORG_ONE_ID,
404
+ role: 'member',
405
+ user_id: 'user-123',
406
+ status: 'active'
407
+ },
408
+ {
409
+ organisation_id: ORG_TWO_ID,
410
+ role: 'member',
411
+ user_id: 'user-123',
412
+ status: 'active'
413
+ }
414
+ ],
415
+ error: null
416
+ });
417
+ }
418
+ return Promise.resolve({ data: null, error: null });
419
+ });
420
+
421
+ render(
422
+ <UnifiedAuthProvider supabaseClient={mockSupabase} appName="TEST_APP">
423
+ <OrganisationProvider>
424
+ <TestOrganisationSelector />
425
+ <TestProtectedPage pageName="dashboard" />
426
+ </OrganisationProvider>
427
+ </UnifiedAuthProvider>
428
+ );
429
+
430
+ // Select organisation
431
+ const orgSelect = screen.getByTestId('org-select');
432
+ await waitForOrganisationOption('Test Organisation 1');
433
+ await user.selectOptions(orgSelect, ORG_ONE_ID);
434
+
435
+ await waitFor(() => {
436
+ expect(screen.getByTestId('selected-org')).toHaveTextContent('Test Organisation 1');
437
+ });
438
+
439
+ // Verify permission check includes organisation context
440
+ await waitFor(() => {
441
+ expect(mockSupabase.rpc).toHaveBeenCalledWith(
442
+ 'rbac_check_permission_simplified',
443
+ expect.objectContaining({
444
+ p_organisation_id: ORG_ONE_ID
445
+ })
446
+ );
447
+ });
448
+ });
449
+ });
450
+ });
451
+
@@ -369,6 +369,10 @@ describe('RBACEngine - Comprehensive Tests', () => {
369
369
  is: vi.fn().mockReturnThis(),
370
370
  lte: vi.fn().mockReturnThis(),
371
371
  or: vi.fn().mockReturnThis(),
372
+ limit: vi.fn().mockResolvedValue({
373
+ data: [{ role: 'org_admin' }],
374
+ error: null
375
+ }),
372
376
  single: vi.fn().mockResolvedValue({
373
377
  data: { role: 'org_admin' },
374
378
  error: null
@@ -432,6 +436,10 @@ describe('RBACEngine - Comprehensive Tests', () => {
432
436
  is: vi.fn().mockReturnThis(),
433
437
  lte: vi.fn().mockReturnThis(),
434
438
  or: vi.fn().mockReturnThis(),
439
+ limit: vi.fn().mockResolvedValue({
440
+ data: [],
441
+ error: { code: 'PGRST116' }
442
+ }),
435
443
  single: vi.fn().mockResolvedValue({
436
444
  data: null,
437
445
  error: { code: 'PGRST116' }
@@ -502,6 +510,10 @@ describe('RBACEngine - Comprehensive Tests', () => {
502
510
  is: vi.fn().mockReturnThis(),
503
511
  lte: vi.fn().mockReturnThis(),
504
512
  or: vi.fn().mockReturnThis(),
513
+ limit: vi.fn().mockResolvedValue({
514
+ data: [],
515
+ error: null
516
+ }),
505
517
  single: vi.fn().mockResolvedValue({
506
518
  data: null,
507
519
  error: { code: 'PGRST116' }
@@ -208,6 +208,10 @@ describe('RBACEngine - Core Logic Tests', () => {
208
208
  is: vi.fn().mockReturnThis(),
209
209
  lte: vi.fn().mockReturnThis(),
210
210
  or: vi.fn().mockReturnThis(),
211
+ limit: vi.fn().mockResolvedValue({
212
+ data: [{ role: 'org_admin' }],
213
+ error: null
214
+ }),
211
215
  single: vi.fn().mockResolvedValue({
212
216
  data: { role: 'org_admin' },
213
217
  error: null
@@ -256,6 +260,10 @@ describe('RBACEngine - Core Logic Tests', () => {
256
260
  is: vi.fn().mockReturnThis(),
257
261
  lte: vi.fn().mockReturnThis(),
258
262
  or: vi.fn().mockReturnThis(),
263
+ limit: vi.fn().mockResolvedValue({
264
+ data: [],
265
+ error: null
266
+ }),
259
267
  single: vi.fn().mockResolvedValue({ data: null, error: null })
260
268
  };
261
269
 
@@ -176,6 +176,10 @@ describe('RBACEngine - Simplified Tests', () => {
176
176
  is: vi.fn().mockReturnThis(),
177
177
  lte: vi.fn().mockReturnThis(),
178
178
  or: vi.fn().mockReturnThis(),
179
+ limit: vi.fn().mockResolvedValue({
180
+ data: [{ role: 'org_admin' }],
181
+ error: null
182
+ }),
179
183
  single: vi.fn().mockResolvedValue({
180
184
  data: { role: 'org_admin' },
181
185
  error: null