@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
@@ -1,741 +0,0 @@
1
- import { renderHook, waitFor } from '@testing-library/react';
2
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
3
- import { TestWrapper, renderWithProviders } from '../../__tests__/helpers/test-utils';
4
- import { useRBAC } from '../../rbac/hooks/useRBAC';
5
-
6
- // Mock the useRBAC hook
7
- vi.mock('../../rbac/hooks/useRBAC');
8
-
9
- // Mock useUnifiedAuth (required by usePermissionCache for user context)
10
- const mockUseUnifiedAuthFn = vi.fn(() => ({
11
- user: { id: 'test-user-id' },
12
- session: null,
13
- appName: 'test-app',
14
- selectedOrganisation: { id: 'test-org-id' },
15
- selectedEvent: null,
16
- selectedOrganisationId: undefined,
17
- selectedEventId: undefined,
18
- supabase: {},
19
- isLoading: false,
20
- error: null,
21
- }));
22
- vi.mock('../../providers/services/UnifiedAuthProvider', () => ({
23
- useUnifiedAuth: () => mockUseUnifiedAuthFn(),
24
- UnifiedAuthProvider: ({ children }: { children: any }) => children,
25
- }));
26
-
27
- // Mock isPermittedCached from RBAC API (used by usePermissionCache)
28
- vi.mock('../../rbac/api', async () => {
29
- const actual = await vi.importActual('../../rbac/api');
30
- return {
31
- ...actual,
32
- isPermittedCached: vi.fn().mockResolvedValue(true),
33
- };
34
- });
35
-
36
- // Mock logger
37
- vi.mock('../../utils/core/logger', () => {
38
- const mockLoggerInstance = {
39
- debug: vi.fn(),
40
- info: vi.fn(),
41
- warn: vi.fn(),
42
- error: vi.fn(),
43
- };
44
- return {
45
- createLogger: vi.fn(() => mockLoggerInstance),
46
- logger: mockLoggerInstance,
47
- };
48
- });
49
-
50
- // Mock useOrganisations hook (required by usePermissionCache)
51
- const mockOrganisationContext = {
52
- selectedOrganisation: {
53
- id: 'test-org-id',
54
- name: 'Test Organisation',
55
- display_name: 'Test Organisation',
56
- slug: 'test-org',
57
- description: 'Test organisation',
58
- subscription_tier: 'basic',
59
- settings: {},
60
- is_active: true,
61
- created_at: '2023-01-01T00:00:00Z',
62
- updated_at: '2023-01-01T00:00:00Z',
63
- },
64
- organisations: [],
65
- userMemberships: [],
66
- isLoading: false,
67
- error: null,
68
- hasValidOrganisationContext: true,
69
- setSelectedOrganisation: vi.fn(),
70
- switchOrganisation: vi.fn().mockResolvedValue(undefined),
71
- getUserRole: vi.fn().mockReturnValue('member'),
72
- validateOrganisationAccess: vi.fn().mockReturnValue(true),
73
- ensureOrganisationContext: vi.fn().mockReturnValue(null),
74
- refreshOrganisations: vi.fn().mockResolvedValue(undefined),
75
- getPrimaryOrganisation: vi.fn().mockReturnValue(null),
76
- isOrganisationSecure: vi.fn().mockReturnValue(true),
77
- };
78
-
79
- vi.mock('../useOrganisations', () => ({
80
- useOrganisations: () => mockOrganisationContext,
81
- }));
82
-
83
- // Mock useEvents hook (optional - wrapped in try/catch in usePermissionCache)
84
- vi.mock('../useEvents', () => ({
85
- useEvents: vi.fn(() => ({
86
- selectedEvent: { event_id: 'event-123' },
87
- events: [],
88
- isLoading: false,
89
- error: null,
90
- })),
91
- }));
92
-
93
- const mockUseRBAC = vi.mocked(useRBAC);
94
-
95
- // Import after mocking
96
- import { usePermissionCache } from '../usePermissionCache';
97
- import { isPermittedCached } from '../../rbac/api';
98
- const mockIsPermittedCached = vi.mocked(isPermittedCached);
99
-
100
- describe('usePermissionCache', () => {
101
- beforeEach(() => {
102
- vi.clearAllMocks();
103
-
104
- // Reset isPermittedCached mock
105
- mockIsPermittedCached.mockClear();
106
- mockIsPermittedCached.mockResolvedValue(true);
107
-
108
- // Default mock implementation
109
- mockUseRBAC.mockReturnValue({
110
- hasPermission: vi.fn().mockResolvedValue(true),
111
- user: { id: 'test-user-id' }
112
- });
113
- });
114
-
115
- afterEach(() => {
116
- vi.clearAllTimers();
117
- });
118
-
119
- describe('Initial Configuration', () => {
120
- it('uses default configuration when no config is provided', () => {
121
- const { result } = renderHook(() => usePermissionCache(), {
122
- wrapper: TestWrapper
123
- });
124
-
125
- expect(result.current).toHaveProperty('checkPermission');
126
- expect(result.current).toHaveProperty('checkMultiplePermissions');
127
- expect(result.current).toHaveProperty('getCachedPermissions');
128
- expect(result.current).toHaveProperty('invalidateCache');
129
- expect(result.current).toHaveProperty('getDebugInfo');
130
- expect(result.current).toHaveProperty('getAuditTrail');
131
- });
132
-
133
- it('merges custom configuration with defaults', () => {
134
- const customConfig = {
135
- defaultTTL: 10000,
136
- maxCacheSize: 500,
137
- enableLogging: true,
138
- enableAuditTrail: false
139
- };
140
-
141
- const { result } = renderHook(() => usePermissionCache(customConfig), {
142
- wrapper: TestWrapper
143
- });
144
-
145
- expect(result.current).toHaveProperty('checkPermission');
146
- expect(result.current).toHaveProperty('checkMultiplePermissions');
147
- });
148
- });
149
-
150
- describe('Single Permission Checking', () => {
151
- it('checks permission and caches result', async () => {
152
- mockIsPermittedCached.mockResolvedValueOnce(true);
153
- mockUseRBAC.mockReturnValue({
154
- hasPermission: vi.fn().mockResolvedValue(true),
155
- user: { id: 'test-user-id' }
156
- });
157
-
158
- const { result } = renderHook(() => usePermissionCache(), {
159
- wrapper: TestWrapper
160
- });
161
-
162
- const permission = await result.current.checkPermission('read', 'dashboard');
163
-
164
- expect(permission).toBe(true);
165
- expect(mockIsPermittedCached).toHaveBeenCalled();
166
- });
167
-
168
- it('returns cached result for subsequent calls', async () => {
169
- mockIsPermittedCached.mockResolvedValueOnce(true);
170
- mockUseRBAC.mockReturnValue({
171
- hasPermission: vi.fn().mockResolvedValue(true),
172
- user: { id: 'test-user-id' }
173
- });
174
-
175
- const { result } = renderHook(() => usePermissionCache(), {
176
- wrapper: TestWrapper
177
- });
178
-
179
- // First call
180
- await result.current.checkPermission('read', 'dashboard');
181
-
182
- // Second call should use cache
183
- await result.current.checkPermission('read', 'dashboard');
184
-
185
- // Should only call isPermittedCached once (second call uses cache)
186
- expect(mockIsPermittedCached).toHaveBeenCalledTimes(1);
187
- });
188
-
189
- it('respects custom TTL', async () => {
190
- mockIsPermittedCached.mockResolvedValueOnce(true);
191
- mockUseRBAC.mockReturnValue({
192
- hasPermission: vi.fn().mockResolvedValue(true),
193
- user: { id: 'test-user-id' }
194
- });
195
-
196
- const { result } = renderHook(() => usePermissionCache({ defaultTTL: 1000 }), {
197
- wrapper: TestWrapper
198
- });
199
-
200
- await result.current.checkPermission('read', 'dashboard', 2000);
201
-
202
- expect(mockIsPermittedCached).toHaveBeenCalled();
203
- });
204
-
205
- it('handles permission check errors gracefully', async () => {
206
- const { logger } = await import('../../utils/core/logger');
207
- mockIsPermittedCached.mockRejectedValueOnce(new Error('Database error'));
208
-
209
- mockUseRBAC.mockReturnValue({
210
- hasPermission: vi.fn().mockResolvedValue(true),
211
- user: { id: 'test-user-id' }
212
- });
213
-
214
- const { result } = renderHook(() => usePermissionCache(), {
215
- wrapper: TestWrapper
216
- });
217
-
218
- const permission = await result.current.checkPermission('read', 'dashboard');
219
-
220
- expect(permission).toBe(false);
221
- // Verify error was logged using logger
222
- expect(logger.error).toHaveBeenCalled();
223
- });
224
- });
225
-
226
- describe('Multiple Permission Checking', () => {
227
- it('checks multiple permissions efficiently', async () => {
228
- mockIsPermittedCached
229
- .mockResolvedValueOnce(true)
230
- .mockResolvedValueOnce(false)
231
- .mockResolvedValueOnce(true)
232
- .mockResolvedValueOnce(false);
233
-
234
- mockUseRBAC.mockReturnValue({
235
- hasPermission: vi.fn().mockResolvedValue(true),
236
- user: { id: 'test-user-id' }
237
- });
238
-
239
-
240
- const { result } = renderHook(() => usePermissionCache(), {
241
- wrapper: TestWrapper
242
- });
243
-
244
- const permissions = await result.current.checkMultiplePermissions([
245
- ['read', 'dashboard'],
246
- ['create', 'dashboard'],
247
- ['update', 'dashboard'],
248
- ['delete', 'dashboard']
249
- ]);
250
-
251
- expect(permissions).toHaveLength(4);
252
- expect(permissions[0]).toEqual({
253
- operation: 'read',
254
- pageId: 'dashboard',
255
- hasPermission: true,
256
- cached: false,
257
- timestamp: expect.any(Number)
258
- });
259
- expect(permissions[1]).toEqual({
260
- operation: 'create',
261
- pageId: 'dashboard',
262
- hasPermission: false,
263
- cached: false,
264
- timestamp: expect.any(Number)
265
- });
266
- });
267
-
268
- it('uses cached results for multiple permission checks', async () => {
269
- mockIsPermittedCached
270
- .mockResolvedValueOnce(true)
271
- .mockResolvedValueOnce(false);
272
-
273
- mockUseRBAC.mockReturnValue({
274
- hasPermission: vi.fn().mockResolvedValue(true),
275
- user: { id: 'test-user-id' }
276
- });
277
-
278
-
279
- const { result } = renderHook(() => usePermissionCache(), {
280
- wrapper: TestWrapper
281
- });
282
-
283
- // First call
284
- await result.current.checkMultiplePermissions([
285
- ['read', 'dashboard'],
286
- ['create', 'dashboard']
287
- ]);
288
-
289
- // Second call should use cache
290
- await result.current.checkMultiplePermissions([
291
- ['read', 'dashboard'],
292
- ['create', 'dashboard']
293
- ]);
294
-
295
- // Should only call isPermittedCached twice (once per permission, second call uses cache)
296
- expect(mockIsPermittedCached).toHaveBeenCalledTimes(2);
297
- });
298
-
299
- it('handles mixed cached and uncached permissions', async () => {
300
- mockIsPermittedCached
301
- .mockResolvedValueOnce(true)
302
- .mockResolvedValueOnce(false)
303
- .mockResolvedValueOnce(true)
304
- .mockResolvedValueOnce(false);
305
-
306
- mockUseRBAC.mockReturnValue({
307
- hasPermission: vi.fn().mockResolvedValue(true),
308
- user: { id: 'test-user-id' }
309
- });
310
-
311
-
312
- const { result } = renderHook(() => usePermissionCache(), {
313
- wrapper: TestWrapper
314
- });
315
-
316
- // Cache first two permissions
317
- await result.current.checkMultiplePermissions([
318
- ['read', 'dashboard'],
319
- ['create', 'dashboard']
320
- ]);
321
-
322
- // Check all four permissions (first two should be cached)
323
- const permissions = await result.current.checkMultiplePermissions([
324
- ['read', 'dashboard'],
325
- ['create', 'dashboard'],
326
- ['update', 'dashboard'],
327
- ['delete', 'dashboard']
328
- ]);
329
-
330
- expect(permissions).toHaveLength(4);
331
- expect(permissions[0].cached).toBe(true);
332
- expect(permissions[1].cached).toBe(true);
333
- expect(permissions[2].cached).toBe(false);
334
- expect(permissions[3].cached).toBe(false);
335
- });
336
- });
337
-
338
- describe('Cache Management', () => {
339
- it('enforces maximum cache size', async () => {
340
- mockIsPermittedCached.mockResolvedValue(true);
341
- mockUseRBAC.mockReturnValue({
342
- hasPermission: vi.fn().mockResolvedValue(true),
343
- user: { id: 'test-user-id' }
344
- });
345
-
346
- const { result } = renderHook(() => usePermissionCache({ maxCacheSize: 3 }), {
347
- wrapper: TestWrapper
348
- });
349
-
350
- // Add more entries than max cache size
351
- await result.current.checkPermission('read', 'page1');
352
- await result.current.checkPermission('read', 'page2');
353
- await result.current.checkPermission('read', 'page3');
354
- await result.current.checkPermission('read', 'page4');
355
- await result.current.checkPermission('read', 'page5');
356
-
357
- // Check that cache size is maintained
358
- const debugInfo = result.current.getDebugInfo();
359
- expect(debugInfo.cacheSize).toBeLessThanOrEqual(3);
360
- });
361
-
362
- it('invalidates cache entries', async () => {
363
- mockIsPermittedCached.mockResolvedValue(true);
364
- mockUseRBAC.mockReturnValue({
365
- hasPermission: vi.fn().mockResolvedValue(true),
366
- user: { id: 'test-user-id' }
367
- });
368
-
369
- const { result } = renderHook(() => usePermissionCache(), {
370
- wrapper: TestWrapper
371
- });
372
-
373
- // Cache some permissions
374
- await result.current.checkPermission('read', 'dashboard');
375
- await result.current.checkPermission('create', 'dashboard');
376
-
377
- // Invalidate all cache
378
- result.current.invalidateCache();
379
-
380
- // Check permissions again (should not use cache)
381
- await result.current.checkPermission('read', 'dashboard');
382
- await result.current.checkPermission('create', 'dashboard');
383
-
384
- // Should be called 4 times (2 initial + 2 after invalidation)
385
- expect(mockIsPermittedCached).toHaveBeenCalledTimes(4);
386
- });
387
-
388
- it('invalidates cache entries by pattern', async () => {
389
- mockIsPermittedCached.mockResolvedValue(true);
390
- mockUseRBAC.mockReturnValue({
391
- hasPermission: vi.fn().mockResolvedValue(true),
392
- user: { id: 'test-user-id' }
393
- });
394
-
395
- const { result } = renderHook(() => usePermissionCache(), {
396
- wrapper: TestWrapper
397
- });
398
-
399
- // Cache permissions for different pages
400
- await result.current.checkPermission('read', 'dashboard');
401
- await result.current.checkPermission('read', 'admin');
402
- await result.current.checkPermission('read', 'users');
403
-
404
- // Invalidate only dashboard permissions
405
- result.current.invalidateCache('dashboard');
406
-
407
- // Check permissions again
408
- await result.current.checkPermission('read', 'dashboard');
409
- await result.current.checkPermission('read', 'admin');
410
-
411
- // dashboard should be called again, admin should use cache
412
- // 3 initial calls + 1 for dashboard after invalidation = 4 total
413
- expect(mockIsPermittedCached).toHaveBeenCalledTimes(4);
414
- });
415
-
416
- it('cleans up expired cache entries', async () => {
417
- vi.useFakeTimers();
418
-
419
- mockIsPermittedCached.mockResolvedValue(true);
420
- mockUseRBAC.mockReturnValue({
421
- hasPermission: vi.fn().mockResolvedValue(true),
422
- user: { id: 'test-user-id' }
423
- });
424
-
425
- const { result } = renderHook(() => usePermissionCache({ defaultTTL: 1000 }), {
426
- wrapper: TestWrapper
427
- });
428
-
429
- // Cache a permission
430
- await result.current.checkPermission('read', 'dashboard');
431
-
432
- // Advance time past TTL
433
- vi.advanceTimersByTime(2000);
434
-
435
- // Trigger cleanup by checking another permission
436
- await result.current.checkPermission('read', 'admin');
437
-
438
- const debugInfo = result.current.getDebugInfo();
439
- expect(debugInfo.cacheSize).toBe(1); // Only the new permission should remain
440
- });
441
- });
442
-
443
- describe('Debug Information', () => {
444
- it('provides accurate debug information', async () => {
445
- mockIsPermittedCached
446
- .mockResolvedValueOnce(true)
447
- .mockResolvedValueOnce(false);
448
-
449
- mockUseRBAC.mockReturnValue({
450
- hasPermission: vi.fn().mockResolvedValue(true),
451
- user: { id: 'test-user-id' }
452
- });
453
-
454
-
455
- const { result } = renderHook(() => usePermissionCache(), {
456
- wrapper: TestWrapper
457
- });
458
-
459
- // Make some permission checks
460
- await result.current.checkPermission('read', 'dashboard');
461
- await result.current.checkPermission('create', 'dashboard');
462
- await result.current.checkPermission('read', 'dashboard'); // This should be cached
463
-
464
- const debugInfo = result.current.getDebugInfo();
465
-
466
- expect(debugInfo.cacheSize).toBe(2);
467
- expect(debugInfo.cacheHits).toBe(1);
468
- expect(debugInfo.cacheMisses).toBe(2);
469
- expect(debugInfo.totalChecks).toBe(3);
470
- expect(debugInfo.averageResponseTime).toBeGreaterThan(0);
471
- expect(debugInfo.lastInvalidation).toBe(0); // No invalidation has been performed yet
472
- });
473
-
474
- it('tracks response times accurately', async () => {
475
- mockIsPermittedCached.mockResolvedValue(true);
476
- mockUseRBAC.mockReturnValue({
477
- hasPermission: vi.fn().mockResolvedValue(true),
478
- user: { id: 'test-user-id' }
479
- });
480
-
481
-
482
- const { result } = renderHook(() => usePermissionCache(), {
483
- wrapper: TestWrapper
484
- });
485
-
486
- await result.current.checkPermission('read', 'dashboard');
487
-
488
- const debugInfo = result.current.getDebugInfo();
489
- // Just verify that response time is tracked (greater than 0)
490
- expect(debugInfo.averageResponseTime).toBeGreaterThan(0);
491
- expect(debugInfo.totalChecks).toBe(1);
492
- });
493
- });
494
-
495
- describe('Audit Trail', () => {
496
- it('records audit trail when enabled', async () => {
497
- mockIsPermittedCached.mockResolvedValue(true);
498
- // The hook uses useUnifiedAuth for user, not useRBAC
499
- mockUseUnifiedAuthFn.mockReturnValue({
500
- user: { id: 'test-user-id' },
501
- session: null,
502
- appName: 'test-app',
503
- selectedOrganisationId: undefined,
504
- selectedEventId: undefined
505
- } as any);
506
- mockUseRBAC.mockReturnValue({
507
- hasPermission: vi.fn().mockResolvedValue(true),
508
- user: { id: 'test-user-id' }
509
- });
510
-
511
- const { result } = renderHook(() => usePermissionCache({ enableAuditTrail: true }), {
512
- wrapper: TestWrapper
513
- });
514
-
515
- await result.current.checkPermission('read', 'dashboard');
516
- await result.current.checkPermission('create', 'admin');
517
-
518
- const auditTrail = result.current.getAuditTrail();
519
-
520
- expect(auditTrail).toHaveLength(2);
521
- // Check all properties including timestamp
522
- expect(auditTrail[0]).toHaveProperty('timestamp');
523
- expect(auditTrail[0]).toHaveProperty('operation', 'read');
524
- expect(auditTrail[0]).toHaveProperty('pageId', 'dashboard');
525
- expect(auditTrail[0]).toHaveProperty('result', true);
526
- expect(auditTrail[0]).toHaveProperty('cached', false);
527
- expect(auditTrail[0]).toHaveProperty('userId', 'test-user-id');
528
- expect(typeof auditTrail[0].timestamp).toBe('number');
529
-
530
- expect(auditTrail[1]).toHaveProperty('timestamp');
531
- expect(auditTrail[1]).toHaveProperty('operation', 'create');
532
- expect(auditTrail[1]).toHaveProperty('pageId', 'admin');
533
- expect(auditTrail[1]).toHaveProperty('result', true);
534
- expect(auditTrail[1]).toHaveProperty('cached', false);
535
- expect(auditTrail[1]).toHaveProperty('userId', 'test-user-id');
536
- expect(typeof auditTrail[1].timestamp).toBe('number');
537
- });
538
-
539
- it('does not record audit trail when disabled', async () => {
540
- mockIsPermittedCached.mockResolvedValue(true);
541
- mockUseRBAC.mockReturnValue({
542
- hasPermission: vi.fn().mockResolvedValue(true),
543
- user: { id: 'test-user-id' }
544
- });
545
-
546
- const { result } = renderHook(() => usePermissionCache({ enableAuditTrail: false }), {
547
- wrapper: TestWrapper
548
- });
549
-
550
- await result.current.checkPermission('read', 'dashboard');
551
-
552
- const auditTrail = result.current.getAuditTrail();
553
- expect(auditTrail).toHaveLength(0);
554
- });
555
-
556
- it('limits audit trail size', async () => {
557
- mockIsPermittedCached.mockResolvedValue(true);
558
- mockUseRBAC.mockReturnValue({
559
- hasPermission: vi.fn().mockResolvedValue(true),
560
- user: { id: 'test-user-id' }
561
- });
562
-
563
- const { result } = renderHook(() => usePermissionCache({ enableAuditTrail: true }), {
564
- wrapper: TestWrapper
565
- });
566
-
567
- // Make more than 1000 permission checks
568
- for (let i = 0; i < 1100; i++) {
569
- await result.current.checkPermission('read', `page${i}`);
570
- }
571
-
572
- const auditTrail = result.current.getAuditTrail();
573
- expect(auditTrail.length).toBeLessThanOrEqual(500);
574
- });
575
- });
576
-
577
- describe('Logging', () => {
578
- it('logs permission checks when enabled', async () => {
579
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
580
-
581
- mockIsPermittedCached.mockResolvedValue(true);
582
- mockUseRBAC.mockReturnValue({
583
- hasPermission: vi.fn().mockResolvedValue(true),
584
- user: { id: 'test-user-id' }
585
- });
586
-
587
- const { result } = renderHook(() => usePermissionCache({ enableLogging: true }), {
588
- wrapper: TestWrapper
589
- });
590
-
591
- // Verify the actual behavior: permission check works correctly
592
- const hasPermission = await result.current.checkPermission('read', 'dashboard');
593
-
594
- // Test the actual functionality: permission is checked and result is returned
595
- expect(hasPermission).toBe(true);
596
- // Verify caching is working: second call should use cache
597
- const hasPermissionCached = await result.current.checkPermission('read', 'dashboard');
598
- expect(hasPermissionCached).toBe(true);
599
- // Verify debug info shows cache hits
600
- const debugInfo = result.current.getDebugInfo();
601
- expect(debugInfo.cacheHits).toBeGreaterThan(0);
602
-
603
- consoleSpy.mockRestore();
604
- });
605
-
606
- it('does not log when disabled', async () => {
607
- const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
608
-
609
- mockIsPermittedCached.mockResolvedValue(true);
610
- mockUseRBAC.mockReturnValue({
611
- hasPermission: vi.fn().mockResolvedValue(true),
612
- user: { id: 'test-user-id' }
613
- });
614
-
615
- const { result } = renderHook(() => usePermissionCache({ enableLogging: false }), {
616
- wrapper: TestWrapper
617
- });
618
-
619
- await result.current.checkPermission('read', 'dashboard');
620
-
621
- expect(consoleSpy).not.toHaveBeenCalledWith(
622
- expect.stringContaining('[PermissionCache]')
623
- );
624
-
625
- consoleSpy.mockRestore();
626
- });
627
- });
628
-
629
- describe('Cached Permissions', () => {
630
- it('returns cached permissions for a page', async () => {
631
- mockIsPermittedCached
632
- .mockResolvedValueOnce(true)
633
- .mockResolvedValueOnce(false)
634
- .mockResolvedValueOnce(true)
635
- .mockResolvedValueOnce(false);
636
-
637
- mockUseRBAC.mockReturnValue({
638
- hasPermission: vi.fn().mockResolvedValue(true),
639
- user: { id: 'test-user-id' }
640
- });
641
-
642
-
643
- const { result } = renderHook(() => usePermissionCache(), {
644
- wrapper: TestWrapper
645
- });
646
-
647
- // Cache all permissions for dashboard
648
- await result.current.checkPermission('read', 'dashboard');
649
- await result.current.checkPermission('create', 'dashboard');
650
- await result.current.checkPermission('update', 'dashboard');
651
- await result.current.checkPermission('delete', 'dashboard');
652
-
653
- const cachedPermissions = result.current.getCachedPermissions('dashboard');
654
-
655
- expect(cachedPermissions).toHaveLength(4);
656
- expect(cachedPermissions).toEqual([
657
- { operation: 'read', hasPermission: true },
658
- { operation: 'create', hasPermission: false },
659
- { operation: 'update', hasPermission: true },
660
- { operation: 'delete', hasPermission: false }
661
- ]);
662
- });
663
-
664
- it('returns empty array for uncached page', async () => {
665
- mockIsPermittedCached.mockResolvedValue(true);
666
- mockUseRBAC.mockReturnValue({
667
- hasPermission: vi.fn().mockResolvedValue(true),
668
- user: { id: 'test-user-id' }
669
- });
670
-
671
- const { result } = renderHook(() => usePermissionCache(), {
672
- wrapper: TestWrapper
673
- });
674
-
675
- const cachedPermissions = result.current.getCachedPermissions('uncached-page');
676
-
677
- expect(cachedPermissions).toHaveLength(0);
678
- });
679
- });
680
-
681
- describe('Edge Cases', () => {
682
- it('handles concurrent permission checks', async () => {
683
- mockIsPermittedCached.mockResolvedValue(true);
684
- mockUseRBAC.mockReturnValue({
685
- hasPermission: vi.fn().mockResolvedValue(true),
686
- user: { id: 'test-user-id' }
687
- });
688
-
689
- const { result } = renderHook(() => usePermissionCache(), {
690
- wrapper: TestWrapper
691
- });
692
-
693
- // Make concurrent permission checks
694
- const promises = [
695
- result.current.checkPermission('read', 'dashboard'),
696
- result.current.checkPermission('read', 'dashboard'),
697
- result.current.checkPermission('read', 'dashboard')
698
- ];
699
-
700
- const results = await Promise.all(promises);
701
-
702
- expect(results).toEqual([true, true, true]);
703
- expect(mockIsPermittedCached).toHaveBeenCalledTimes(1); // Should only call once due to caching
704
- });
705
-
706
- it('handles rapid cache invalidation', async () => {
707
- mockIsPermittedCached.mockResolvedValue(true);
708
- mockUseRBAC.mockReturnValue({
709
- hasPermission: vi.fn().mockResolvedValue(true),
710
- user: { id: 'test-user-id' }
711
- });
712
-
713
- const { result } = renderHook(() => usePermissionCache(), {
714
- wrapper: TestWrapper
715
- });
716
-
717
- await result.current.checkPermission('read', 'dashboard');
718
- result.current.invalidateCache();
719
- await result.current.checkPermission('read', 'dashboard');
720
-
721
- expect(mockIsPermittedCached).toHaveBeenCalledTimes(2);
722
- });
723
-
724
- it('handles empty permission arrays', async () => {
725
- mockIsPermittedCached.mockResolvedValue(true);
726
- mockUseRBAC.mockReturnValue({
727
- hasPermission: vi.fn().mockResolvedValue(true),
728
- user: { id: 'test-user-id' }
729
- });
730
-
731
- const { result } = renderHook(() => usePermissionCache(), {
732
- wrapper: TestWrapper
733
- });
734
-
735
- const permissions = await result.current.checkMultiplePermissions([]);
736
-
737
- expect(permissions).toHaveLength(0);
738
- expect(mockIsPermittedCached).not.toHaveBeenCalled();
739
- });
740
- });
741
- });