@jmruthers/pace-core 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (502) hide show
  1. package/CHANGELOG.md +43 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +18 -91
  3. package/cursor-rules/01-standards-compliance.mdc +16 -47
  4. package/cursor-rules/02-project-structure.mdc +4 -4
  5. package/cursor-rules/03-solid-principles.mdc +45 -164
  6. package/cursor-rules/04-testing-standards.mdc +22 -69
  7. package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
  8. package/cursor-rules/06-code-quality.mdc +42 -125
  9. package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
  10. package/cursor-rules/08-markup-quality.mdc +452 -0
  11. package/cursor-rules/CHANGELOG.md +18 -0
  12. package/cursor-rules/README.md +2 -1
  13. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-TPTKCX4D.js} +10 -9
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +356 -111
  17. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  18. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  19. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  20. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  21. package/dist/chunk-24UVZUZG.js.map +1 -0
  22. package/dist/{chunk-4N5C5XZU.js → chunk-2UOI2FG5.js} +4 -4
  23. package/dist/chunk-2UOI2FG5.js.map +1 -0
  24. package/dist/{chunk-T33XF5ZC.js → chunk-3XC4CPTD.js} +4317 -3963
  25. package/dist/chunk-3XC4CPTD.js.map +1 -0
  26. package/dist/{chunk-4ZC4GX36.js → chunk-6J4GEEJR.js} +172 -45
  27. package/dist/chunk-6J4GEEJR.js.map +1 -0
  28. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  29. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-EHMR7VYL.js} +4 -4
  31. package/dist/chunk-EHMR7VYL.js.map +1 -0
  32. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  33. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  34. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  35. package/dist/chunk-FFQEQTNW.js.map +1 -0
  36. package/dist/chunk-FMUCXFII.js +76 -0
  37. package/dist/chunk-FMUCXFII.js.map +1 -0
  38. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  39. package/dist/chunk-L4OXEN46.js.map +1 -0
  40. package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
  41. package/dist/chunk-M43Y4SSO.js.map +1 -0
  42. package/dist/{chunk-3XTALGJF.js → chunk-MMZ7JXPU.js} +60 -223
  43. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  44. package/dist/{chunk-GLK6VM3F.js → chunk-NECFR5MM.js} +254 -170
  45. package/dist/chunk-NECFR5MM.js.map +1 -0
  46. package/dist/{chunk-JBKQ3SAO.js → chunk-SFZUDBL5.js} +40 -4
  47. package/dist/chunk-SFZUDBL5.js.map +1 -0
  48. package/dist/{chunk-XM25TVIE.js → chunk-XWQCNGTQ.js} +724 -363
  49. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  50. package/dist/components.d.ts +5 -5
  51. package/dist/components.js +14 -11
  52. package/dist/components.js.map +1 -1
  53. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  54. package/dist/hooks.d.ts +55 -122
  55. package/dist/hooks.js +8 -12
  56. package/dist/hooks.js.map +1 -1
  57. package/dist/index.d.ts +60 -13
  58. package/dist/index.js +19 -19
  59. package/dist/index.js.map +1 -1
  60. package/dist/providers.d.ts +21 -3
  61. package/dist/providers.js +2 -2
  62. package/dist/rbac/index.d.ts +145 -114
  63. package/dist/rbac/index.js +8 -11
  64. package/dist/theming/runtime.d.ts +1 -13
  65. package/dist/theming/runtime.js +1 -1
  66. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  67. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  68. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  69. package/dist/types.d.ts +2 -2
  70. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +31 -1
  71. package/dist/utils.d.ts +4 -5
  72. package/dist/utils.js +14 -14
  73. package/dist/utils.js.map +1 -1
  74. package/docs/api/README.md +7 -1
  75. package/docs/api/classes/ColumnFactory.md +8 -8
  76. package/docs/api/classes/InvalidScopeError.md +4 -4
  77. package/docs/api/classes/Logger.md +1 -1
  78. package/docs/api/classes/MissingUserContextError.md +4 -4
  79. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  80. package/docs/api/classes/PermissionDeniedError.md +4 -4
  81. package/docs/api/classes/RBACAuditManager.md +1 -1
  82. package/docs/api/classes/RBACCache.md +1 -1
  83. package/docs/api/classes/RBACEngine.md +1 -1
  84. package/docs/api/classes/RBACError.md +4 -4
  85. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  86. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  87. package/docs/api/classes/StorageUtils.md +1 -1
  88. package/docs/api/enums/FileCategory.md +1 -1
  89. package/docs/api/enums/LogLevel.md +1 -1
  90. package/docs/api/enums/RBACErrorCode.md +1 -1
  91. package/docs/api/enums/RPCFunction.md +1 -1
  92. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  93. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  94. package/docs/api/interfaces/AggregateConfig.md +4 -4
  95. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  96. package/docs/api/interfaces/AvatarProps.md +1 -1
  97. package/docs/api/interfaces/BadgeProps.md +9 -2
  98. package/docs/api/interfaces/ButtonProps.md +7 -4
  99. package/docs/api/interfaces/CalendarProps.md +8 -5
  100. package/docs/api/interfaces/CardProps.md +8 -5
  101. package/docs/api/interfaces/ColorPalette.md +1 -1
  102. package/docs/api/interfaces/ColorShade.md +1 -1
  103. package/docs/api/interfaces/ComplianceResult.md +1 -1
  104. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  105. package/docs/api/interfaces/DataRecord.md +1 -1
  106. package/docs/api/interfaces/DataTableAction.md +24 -21
  107. package/docs/api/interfaces/DataTableColumn.md +31 -31
  108. package/docs/api/interfaces/DataTableProps.md +1 -1
  109. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  110. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  111. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  112. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  113. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  114. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  115. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  116. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  117. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  118. package/docs/api/interfaces/ExportColumn.md +1 -1
  119. package/docs/api/interfaces/ExportOptions.md +8 -8
  120. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  121. package/docs/api/interfaces/FileMetadata.md +1 -1
  122. package/docs/api/interfaces/FileReference.md +1 -1
  123. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  124. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  125. package/docs/api/interfaces/FileUploadProps.md +26 -23
  126. package/docs/api/interfaces/FooterProps.md +10 -8
  127. package/docs/api/interfaces/FormFieldProps.md +10 -10
  128. package/docs/api/interfaces/FormProps.md +1 -1
  129. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  130. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  131. package/docs/api/interfaces/InputProps.md +7 -4
  132. package/docs/api/interfaces/LabelProps.md +1 -1
  133. package/docs/api/interfaces/LoggerConfig.md +1 -1
  134. package/docs/api/interfaces/LoginFormProps.md +14 -11
  135. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  136. package/docs/api/interfaces/NavigationContextType.md +1 -1
  137. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  138. package/docs/api/interfaces/NavigationItem.md +11 -11
  139. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  140. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  141. package/docs/api/interfaces/Organisation.md +1 -1
  142. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  143. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  144. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  145. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  146. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  147. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  148. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  149. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  150. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  151. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  152. package/docs/api/interfaces/PaletteData.md +1 -1
  153. package/docs/api/interfaces/ParsedAddress.md +1 -1
  154. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  155. package/docs/api/interfaces/ProgressProps.md +1 -1
  156. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  157. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  158. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  159. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  160. package/docs/api/interfaces/QuickFix.md +1 -1
  161. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  162. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  163. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  164. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  165. package/docs/api/interfaces/RBACConfig.md +1 -1
  166. package/docs/api/interfaces/RBACContext.md +1 -1
  167. package/docs/api/interfaces/RBACLogger.md +1 -1
  168. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  169. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  170. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  171. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  172. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  173. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  174. package/docs/api/interfaces/RBACResult.md +1 -1
  175. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  176. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  177. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  178. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  181. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  182. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  183. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  184. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  185. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  186. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  187. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  188. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  189. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  190. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  191. package/docs/api/interfaces/RouteConfig.md +1 -1
  192. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  193. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  194. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  195. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  196. package/docs/api/interfaces/SetupIssue.md +1 -1
  197. package/docs/api/interfaces/StorageConfig.md +1 -1
  198. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  199. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  200. package/docs/api/interfaces/StorageListOptions.md +1 -1
  201. package/docs/api/interfaces/StorageListResult.md +1 -1
  202. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  203. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  204. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  205. package/docs/api/interfaces/StyleImport.md +1 -1
  206. package/docs/api/interfaces/SwitchProps.md +1 -1
  207. package/docs/api/interfaces/TabsContentProps.md +1 -1
  208. package/docs/api/interfaces/TabsListProps.md +1 -1
  209. package/docs/api/interfaces/TabsProps.md +1 -1
  210. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  211. package/docs/api/interfaces/TextareaProps.md +1 -1
  212. package/docs/api/interfaces/ToastActionElement.md +4 -1
  213. package/docs/api/interfaces/ToastProps.md +1 -1
  214. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  215. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  216. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  217. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  218. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  219. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  220. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  221. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  222. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  223. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  224. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  225. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  226. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  227. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  228. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  229. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  230. package/docs/api/interfaces/UserEventAccess.md +14 -11
  231. package/docs/api/interfaces/UserMenuProps.md +8 -6
  232. package/docs/api/interfaces/UserProfile.md +1 -1
  233. package/docs/api/modules.md +575 -634
  234. package/docs/architecture/database-schema-requirements.md +161 -0
  235. package/docs/core-concepts/rbac-system.md +3 -3
  236. package/docs/documentation-index.md +2 -4
  237. package/docs/getting-started/cursor-rules.md +2 -1
  238. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  239. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  240. package/docs/migration/README.md +52 -6
  241. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  242. package/docs/migration/database-changes-december-2025.md +3 -3
  243. package/docs/rbac/event-based-apps.md +1 -1
  244. package/docs/rbac/getting-started.md +1 -1
  245. package/docs/rbac/quick-start.md +1 -1
  246. package/docs/standards/README.md +1 -0
  247. package/package.json +2 -1
  248. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  249. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  250. package/scripts/audit/core/checks/bundle.cjs +142 -0
  251. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +714 -687
  252. package/scripts/audit/core/checks/config.cjs +54 -0
  253. package/scripts/audit/core/checks/coverage.cjs +84 -0
  254. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  255. package/scripts/audit/core/checks/documentation.cjs +203 -0
  256. package/scripts/audit/core/checks/environment.cjs +128 -0
  257. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  258. package/scripts/audit/core/checks/forms.cjs +172 -0
  259. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  260. package/scripts/audit/core/checks/hooks.cjs +334 -0
  261. package/scripts/audit/core/checks/imports.cjs +244 -0
  262. package/scripts/audit/core/checks/performance.cjs +325 -0
  263. package/scripts/audit/core/checks/routes.cjs +117 -0
  264. package/scripts/audit/core/checks/state.cjs +130 -0
  265. package/scripts/audit/core/checks/structure.cjs +65 -0
  266. package/scripts/audit/core/checks/style.cjs +584 -0
  267. package/scripts/audit/core/checks/testing.cjs +122 -0
  268. package/scripts/audit/core/checks/typescript.cjs +61 -0
  269. package/scripts/audit/core/scanner.cjs +199 -0
  270. package/scripts/audit/core/utils.cjs +137 -0
  271. package/scripts/audit/index.cjs +223 -0
  272. package/scripts/audit/reporters/console.cjs +151 -0
  273. package/scripts/audit/reporters/json.cjs +54 -0
  274. package/scripts/audit/reporters/markdown.cjs +124 -0
  275. package/scripts/audit-consuming-app.cjs +61 -936
  276. package/scripts/build-docs/build-decision.js +240 -0
  277. package/scripts/build-docs/cache-utils.js +105 -0
  278. package/scripts/build-docs/content-normalization.js +150 -0
  279. package/scripts/build-docs/file-utils.js +105 -0
  280. package/scripts/build-docs/git-utils.js +86 -0
  281. package/scripts/build-docs/hash-utils.js +116 -0
  282. package/scripts/build-docs/typedoc-runner.js +220 -0
  283. package/scripts/build-docs-incremental.js +77 -913
  284. package/scripts/utils/command-runner.js +16 -11
  285. package/scripts/validate-formats.js +61 -56
  286. package/scripts/validate-master.js +74 -69
  287. package/scripts/validate-pre-publish.js +70 -65
  288. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  289. package/src/components/Alert/Alert.test.tsx +12 -18
  290. package/src/components/Alert/Alert.tsx +5 -7
  291. package/src/components/Avatar/Avatar.test.tsx +4 -4
  292. package/src/components/Badge/Badge.tsx +14 -0
  293. package/src/components/Button/Button.tsx +22 -0
  294. package/src/components/Calendar/Calendar.tsx +8 -2
  295. package/src/components/Card/Card.tsx +4 -0
  296. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  297. package/src/components/Checkbox/Checkbox.tsx +2 -2
  298. package/src/components/DataTable/DataTable.tsx +38 -4
  299. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  300. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  301. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  302. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  303. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  304. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  305. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  306. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  307. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  308. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  309. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  310. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  311. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  312. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  313. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  314. package/src/components/DataTable/components/EditFields.tsx +307 -0
  315. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  316. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  317. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  318. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  319. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  320. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  321. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  322. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  323. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  324. package/src/components/DataTable/components/UnifiedTableBody.tsx +61 -849
  325. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  326. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  327. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  328. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  329. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  330. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  331. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  332. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  333. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  334. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  335. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  336. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  337. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  338. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  339. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  340. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  341. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  342. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  343. package/src/components/DataTable/styles.ts +6 -6
  344. package/src/components/DataTable/types.ts +6 -10
  345. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  346. package/src/components/DataTable/utils/debugTools.ts +18 -113
  347. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  348. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  349. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  350. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  351. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  352. package/src/components/Dialog/Dialog.tsx +2 -2
  353. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  354. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  355. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  356. package/src/components/ErrorBoundary/index.ts +27 -2
  357. package/src/components/EventSelector/EventSelector.tsx +3 -0
  358. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  359. package/src/components/FileUpload/FileUpload.tsx +22 -2
  360. package/src/components/Footer/Footer.test.tsx +16 -16
  361. package/src/components/Footer/Footer.tsx +14 -11
  362. package/src/components/Form/Form.tsx +1 -0
  363. package/src/components/Header/Header.tsx +21 -10
  364. package/src/components/Input/Input.test.tsx +2 -2
  365. package/src/components/Input/Input.tsx +8 -4
  366. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  367. package/src/components/LoginForm/LoginForm.tsx +4 -0
  368. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  369. package/src/components/NavigationMenu/types.ts +56 -0
  370. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  371. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  372. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -2
  373. package/src/components/PaceAppLayout/PaceAppLayout.tsx +32 -11
  374. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  375. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  376. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  377. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  378. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  379. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  380. package/src/components/Select/Select.tsx +80 -434
  381. package/src/components/Select/context.ts +23 -0
  382. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  383. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  384. package/src/components/Select/hooks/useSelectState.ts +104 -0
  385. package/src/components/Select/index.ts +9 -1
  386. package/src/components/Select/types.ts +123 -0
  387. package/src/components/Select/utils/text.ts +26 -0
  388. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  389. package/src/components/Switch/Switch.tsx +4 -4
  390. package/src/components/Tabs/Tabs.tsx +1 -1
  391. package/src/components/Toast/Toast.tsx +4 -0
  392. package/src/components/Tooltip/Tooltip.tsx +2 -2
  393. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  394. package/src/components/UserMenu/UserMenu.tsx +21 -18
  395. package/src/components/index.ts +2 -2
  396. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  397. package/src/hooks/index.ts +1 -2
  398. package/src/hooks/public/usePublicEvent.ts +4 -0
  399. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  400. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  401. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  402. package/src/hooks/services/useAuth.ts +32 -0
  403. package/src/hooks/services/useCurrentEvent.ts +6 -0
  404. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  405. package/src/hooks/useDebounce.ts +9 -0
  406. package/src/hooks/useEventTheme.ts +6 -0
  407. package/src/hooks/useFileDisplay.ts +4 -0
  408. package/src/hooks/useFileReference.ts +25 -7
  409. package/src/hooks/useFileUrl.ts +11 -1
  410. package/src/hooks/useFocusManagement.ts +14 -0
  411. package/src/hooks/useFocusTrap.ts +3 -0
  412. package/src/hooks/useInactivityTracker.ts +3 -0
  413. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  414. package/src/hooks/useOrganisationPermissions.ts +4 -0
  415. package/src/hooks/useOrganisationSecurity.ts +4 -0
  416. package/src/hooks/usePerformanceMonitor.ts +4 -0
  417. package/src/hooks/usePermissionCache.ts +7 -0
  418. package/src/hooks/useQueryCache.ts +12 -1
  419. package/src/hooks/useSessionRestoration.ts +4 -0
  420. package/src/hooks/useStorage.ts +4 -0
  421. package/src/hooks/useToast.ts +1 -1
  422. package/src/index.ts +2 -1
  423. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  424. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  425. package/src/providers/services/EventServiceProvider.tsx +18 -0
  426. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  427. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  428. package/src/providers/services/UnifiedAuthProvider.tsx +36 -0
  429. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  430. package/src/rbac/README.md +1 -1
  431. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +2 -2
  432. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  433. package/src/rbac/adapters.tsx +14 -5
  434. package/src/rbac/api.ts +100 -67
  435. package/src/rbac/components/NavigationProvider.tsx +4 -1
  436. package/src/rbac/components/PagePermissionGuard.tsx +157 -17
  437. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  438. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  439. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  440. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  441. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  442. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  443. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  444. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  445. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  446. package/src/rbac/engine.ts +38 -14
  447. package/src/rbac/hooks/permissions/index.ts +7 -0
  448. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  449. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  450. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  451. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  452. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  453. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  454. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  455. package/src/rbac/hooks/useCan.test.ts +71 -64
  456. package/src/rbac/hooks/usePermissions.ts +14 -995
  457. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  458. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  459. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  460. package/src/rbac/permissions.ts +0 -30
  461. package/src/rbac/secureClient.ts +200 -61
  462. package/src/rbac/types.ts +8 -0
  463. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  464. package/src/theming/parseEventColours.ts +5 -19
  465. package/src/types/vitest-globals.d.ts +51 -26
  466. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  467. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  468. package/src/utils/__tests__/index.unit.test.ts +2 -2
  469. package/src/utils/audit/audit.ts +0 -3
  470. package/src/utils/core/cn.ts +1 -1
  471. package/src/utils/file-reference/index.ts +53 -1
  472. package/src/utils/formatting/formatting.ts +8 -18
  473. package/src/utils/index.ts +0 -1
  474. package/dist/chunk-3QRJFVBR.js.map +0 -1
  475. package/dist/chunk-3XTALGJF.js.map +0 -1
  476. package/dist/chunk-4N5C5XZU.js.map +0 -1
  477. package/dist/chunk-4ZC4GX36.js.map +0 -1
  478. package/dist/chunk-BYFSK72L.js.map +0 -1
  479. package/dist/chunk-EXUD6RNJ.js +0 -451
  480. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  481. package/dist/chunk-GLK6VM3F.js.map +0 -1
  482. package/dist/chunk-I7PSE6JW.js.map +0 -1
  483. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  484. package/dist/chunk-KNC55RTG.js.map +0 -1
  485. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  486. package/dist/chunk-R77UEZ4E.js.map +0 -1
  487. package/dist/chunk-SQGMNID3.js.map +0 -1
  488. package/dist/chunk-T33XF5ZC.js.map +0 -1
  489. package/dist/chunk-XM25TVIE.js.map +0 -1
  490. package/docs/api/classes/ErrorBoundary.md +0 -144
  491. package/docs/migration/quick-migration-guide.md +0 -356
  492. package/docs/migration/service-architecture.md +0 -281
  493. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  494. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  495. package/src/hooks/useSecureDataAccess.ts +0 -681
  496. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  497. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  498. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  499. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  500. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  501. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  502. /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
@@ -1,559 +0,0 @@
1
- /**
2
- * @file Secure Data Access Hook Tests
3
- * @package @jmruthers/pace-core
4
- * @module Hooks/useSecureDataAccess
5
- * @since 0.4.0
6
- *
7
- * Comprehensive tests for the useSecureDataAccess hook covering all critical functionality.
8
- */
9
-
10
- import { renderHook, waitFor } from '@testing-library/react';
11
- import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
12
- import { useSecureDataAccess } from './useSecureDataAccess';
13
- import { useUnifiedAuth } from '../providers';
14
- import { useOrganisations } from './useOrganisations';
15
- import { setOrganisationContext } from '../utils/context/organisationContext';
16
- import { createMockSupabaseClient, createMockQueryBuilderWithData } from '../__tests__/helpers/supabaseMock';
17
- import { useResolvedScope } from '../rbac/hooks/useResolvedScope';
18
- import { useOrganisationSecurity } from './useOrganisationSecurity';
19
-
20
- // Mock the providers
21
- vi.mock('../providers', () => ({
22
- useUnifiedAuth: vi.fn()
23
- }));
24
-
25
- vi.mock('./useOrganisations', () => ({
26
- useOrganisations: vi.fn()
27
- }));
28
-
29
- // Mock the organisation context utility
30
- vi.mock('../utils/context/organisationContext', () => ({
31
- setOrganisationContext: vi.fn()
32
- }));
33
-
34
- // Mock useResolvedScope
35
- vi.mock('../rbac/hooks/useResolvedScope', () => ({
36
- useResolvedScope: vi.fn()
37
- }));
38
-
39
- // Mock useOrganisationSecurity
40
- vi.mock('./useOrganisationSecurity', () => ({
41
- useOrganisationSecurity: vi.fn()
42
- }));
43
-
44
-
45
- describe('useSecureDataAccess', () => {
46
- const mockUseUnifiedAuth = vi.mocked(useUnifiedAuth);
47
- const mockUseOrganisations = vi.mocked(useOrganisations);
48
- const mockSetOrganisationContext = vi.mocked(setOrganisationContext);
49
- const mockUseResolvedScope = vi.mocked(useResolvedScope);
50
- const mockUseOrganisationSecurity = vi.mocked(useOrganisationSecurity);
51
-
52
- const mockUser = {
53
- id: 'user-123',
54
- email: 'test@example.com'
55
- };
56
-
57
- const mockSession = {
58
- access_token: 'token-123',
59
- refresh_token: 'refresh-123'
60
- };
61
-
62
- // Create proper thenable mock query builder
63
- const mockQueryBuilder = createMockQueryBuilderWithData([{ id: 'record-123' }]);
64
- const mockSupabase = createMockSupabaseClient([{ id: 'record-123' }]);
65
-
66
-
67
- const mockSelectedOrganisation = {
68
- id: 'org-123',
69
- name: 'Test Organisation'
70
- };
71
-
72
- let freshMockQueryBuilder: any;
73
- let freshMockSupabase: any;
74
-
75
- beforeEach(() => {
76
- vi.clearAllMocks();
77
-
78
- // Create a new query builder that we can track
79
- freshMockQueryBuilder = createMockQueryBuilderWithData([{ id: 'record-123' }]);
80
-
81
- // Create Supabase client mock that returns our tracked builder
82
- freshMockSupabase = {
83
- from: vi.fn().mockReturnValue(freshMockQueryBuilder),
84
- rpc: vi.fn().mockResolvedValue({ data: { result: 'success' }, error: null }),
85
- auth: {
86
- getUser: vi.fn().mockResolvedValue({ data: { user: null }, error: null }),
87
- getSession: vi.fn().mockResolvedValue({ data: { session: null }, error: null }),
88
- signIn: vi.fn().mockResolvedValue({ data: { user: null, session: null }, error: null }),
89
- signOut: vi.fn().mockResolvedValue({ error: null }),
90
- onAuthStateChange: vi.fn().mockReturnValue({ data: { subscription: { unsubscribe: vi.fn() } } })
91
- }
92
- };
93
-
94
- mockUseUnifiedAuth.mockReturnValue({
95
- user: mockUser,
96
- session: mockSession,
97
- supabase: freshMockSupabase,
98
- isAuthenticated: true,
99
- signOut: vi.fn(),
100
- // Add other required properties
101
- } as any);
102
-
103
- mockUseOrganisations.mockReturnValue({
104
- selectedOrganisation: mockSelectedOrganisation,
105
- getUserRole: vi.fn().mockReturnValue('member'),
106
- validateOrganisationAccess: vi.fn().mockResolvedValue(true),
107
- ensureOrganisationContext: vi.fn().mockReturnValue(mockSelectedOrganisation),
108
- // Add other required properties
109
- } as any);
110
-
111
- // Mock useResolvedScope to return resolved scope with organisationId
112
- mockUseResolvedScope.mockReturnValue({
113
- resolvedScope: {
114
- organisationId: 'org-123',
115
- eventId: undefined,
116
- appId: undefined,
117
- },
118
- isLoading: false,
119
- error: null,
120
- });
121
-
122
- // Mock useOrganisationSecurity to return not super admin
123
- mockUseOrganisationSecurity.mockReturnValue({
124
- superAdminContext: { isSuperAdmin: false, hasGlobalAccess: false, canManageAllOrganisations: false },
125
- validateOrganisationAccess: vi.fn(),
126
- hasMinimumRole: vi.fn(),
127
- canAccessChildOrganisations: vi.fn(),
128
- checkPermission: vi.fn(),
129
- getPermissions: vi.fn(),
130
- logOrganisationAccess: vi.fn(),
131
- canManageOrganisation: vi.fn(),
132
- } as any);
133
- });
134
-
135
- describe('Hook Initialization', () => {
136
- it('initializes with required dependencies', () => {
137
- const { result } = renderHook(() => useSecureDataAccess());
138
-
139
- expect(result.current).toBeDefined();
140
- expect(result.current.secureQuery).toBeInstanceOf(Function);
141
- expect(result.current.secureInsert).toBeInstanceOf(Function);
142
- expect(result.current.secureUpdate).toBeInstanceOf(Function);
143
- expect(result.current.secureDelete).toBeInstanceOf(Function);
144
- expect(result.current.secureRpc).toBeInstanceOf(Function);
145
- expect(result.current.getCurrentOrganisationId()).toBe('org-123');
146
- });
147
-
148
- it('depends on useUnifiedAuth hook', () => {
149
- renderHook(() => useSecureDataAccess());
150
- expect(mockUseUnifiedAuth).toHaveBeenCalled();
151
- });
152
-
153
- it('depends on useResolvedScope hook', () => {
154
- renderHook(() => useSecureDataAccess());
155
- // useSecureDataAccess now uses useResolvedScope instead of useOrganisations
156
- expect(mockUseResolvedScope).toHaveBeenCalled();
157
- });
158
- });
159
-
160
- describe('Secure Query Operations', () => {
161
- it('executes secure query with organisation filtering', async () => {
162
- const { result } = renderHook(() => useSecureDataAccess());
163
-
164
- const data = await result.current.secureQuery('core_events', '*', { active: true });
165
-
166
- expect(freshMockSupabase.from).toHaveBeenCalledWith('core_events');
167
- expect(freshMockQueryBuilder.select).toHaveBeenCalledWith('*');
168
- expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
169
- expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('active', true);
170
- });
171
-
172
- it('executes secure query with custom filters', async () => {
173
- const { result } = renderHook(() => useSecureDataAccess());
174
-
175
- const data = await result.current.secureQuery('users', '*', { active: true });
176
-
177
- expect(freshMockSupabase.from).toHaveBeenCalledWith('users');
178
- expect(freshMockQueryBuilder.select).toHaveBeenCalledWith('*');
179
- expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('active', true);
180
- });
181
-
182
- it('executes secure query with ordering', async () => {
183
- const { result } = renderHook(() => useSecureDataAccess());
184
-
185
- const data = await result.current.secureQuery('users', '*', {}, {
186
- orderBy: 'created_at',
187
- ascending: false
188
- });
189
-
190
- expect(freshMockQueryBuilder.select).toHaveBeenCalledWith('*');
191
- });
192
-
193
- it('executes secure query with pagination', async () => {
194
- const paginatedData = Array.from({ length: 10 }, (_, i) => ({ id: `record-${i + 20}` }));
195
-
196
- // Ensure range() returns a thenable that resolves with paginated data
197
- // and that all chain methods (eq, select, etc.) maintain the range function
198
- freshMockQueryBuilder.range = vi.fn().mockImplementation(function(min: number, max: number) {
199
- const rangedBuilder = Object.assign({}, this, {
200
- then: vi.fn().mockImplementation((resolve) => {
201
- resolve({ data: paginatedData, error: null });
202
- }),
203
- catch: vi.fn(),
204
- finally: vi.fn(),
205
- range: freshMockQueryBuilder.range // Maintain range for further chaining if needed
206
- });
207
- return rangedBuilder;
208
- });
209
-
210
- // Ensure eq() returns this which has range()
211
- freshMockQueryBuilder.eq = vi.fn().mockReturnThis();
212
- freshMockQueryBuilder.select = vi.fn().mockReturnThis();
213
- freshMockQueryBuilder.limit = vi.fn().mockReturnThis();
214
-
215
- const { result } = renderHook(() => useSecureDataAccess());
216
-
217
- const data = await result.current.secureQuery('users', '*', {}, {
218
- limit: 10,
219
- offset: 20
220
- });
221
-
222
- expect(freshMockQueryBuilder.select).toHaveBeenCalledWith('*');
223
- expect(freshMockQueryBuilder.range).toHaveBeenCalledWith(20, 29); // offset to (offset + limit - 1)
224
- expect(data).toEqual(paginatedData);
225
- });
226
-
227
- it('handles query errors gracefully', async () => {
228
- // Create a query builder that rejects
229
- const errorBuilder = createMockQueryBuilderWithData([], new Error('Query failed'));
230
- freshMockSupabase.from.mockReturnValue(errorBuilder);
231
-
232
- const { result } = renderHook(() => useSecureDataAccess());
233
-
234
- await expect(result.current.secureQuery('users', '*')).rejects.toThrow('Query failed');
235
- });
236
- });
237
-
238
- describe('Secure Insert Operations', () => {
239
- it('executes secure insert with organisation context', async () => {
240
- const { result } = renderHook(() => useSecureDataAccess());
241
-
242
- const data = await result.current.secureInsert('users', { name: 'Test User' });
243
-
244
- expect(freshMockSupabase.from).toHaveBeenCalledWith('users');
245
- expect(freshMockQueryBuilder.insert).toHaveBeenCalledWith({
246
- name: 'Test User',
247
- organisation_id: 'org-123'
248
- });
249
- });
250
-
251
- it('handles insert errors gracefully', async () => {
252
- // Create a query builder that rejects on single()
253
- const errorBuilder = createMockQueryBuilderWithData(null, new Error('Insert failed'));
254
- freshMockSupabase.from.mockReturnValue(errorBuilder);
255
-
256
- const { result } = renderHook(() => useSecureDataAccess());
257
-
258
- await expect(result.current.secureInsert('users', { name: 'Test User' })).rejects.toThrow('Insert failed');
259
- });
260
-
261
- it('prevents organisation_id override', async () => {
262
- const { result } = renderHook(() => useSecureDataAccess());
263
-
264
- const data = await result.current.secureInsert('users', {
265
- name: 'Test User',
266
- organisation_id: 'malicious-org-id'
267
- });
268
-
269
- expect(freshMockQueryBuilder.insert).toHaveBeenCalledWith({
270
- name: 'Test User',
271
- organisation_id: 'org-123' // Should override malicious value
272
- });
273
- });
274
- });
275
-
276
- describe('Secure Update Operations', () => {
277
- it('executes secure update with organisation filtering', async () => {
278
- const { result } = renderHook(() => useSecureDataAccess());
279
-
280
- const data = await result.current.secureUpdate('core_events', { event_name: 'Updated Event' }, { id: 'event-123' });
281
-
282
- expect(freshMockSupabase.from).toHaveBeenCalledWith('core_events');
283
- expect(freshMockQueryBuilder.update).toHaveBeenCalledWith({ event_name: 'Updated Event' });
284
- expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('id', 'event-123');
285
- expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
286
- });
287
-
288
- it('handles update errors gracefully', async () => {
289
- // Create a query builder that rejects
290
- const errorBuilder = createMockQueryBuilderWithData([], new Error('Update failed'));
291
- freshMockSupabase.from.mockReturnValue(errorBuilder);
292
-
293
- const { result } = renderHook(() => useSecureDataAccess());
294
-
295
- await expect(result.current.secureUpdate('users', { name: 'Updated User' }, { id: 'user-123' })).rejects.toThrow('Update failed');
296
- });
297
-
298
- it('prevents organisation_id updates', async () => {
299
- const { result } = renderHook(() => useSecureDataAccess());
300
-
301
- const data = await result.current.secureUpdate('users', {
302
- name: 'Updated User',
303
- organisation_id: 'malicious-org-id'
304
- }, { id: 'user-123' });
305
-
306
- expect(freshMockQueryBuilder.update).toHaveBeenCalledWith({
307
- name: 'Updated User'
308
- // organisation_id should be filtered out
309
- });
310
- });
311
- });
312
-
313
- describe('Secure Delete Operations', () => {
314
- it('executes secure delete with organisation filtering', async () => {
315
- const { result } = renderHook(() => useSecureDataAccess());
316
-
317
- await result.current.secureDelete('core_events', { id: 'event-123' });
318
-
319
- expect(freshMockSupabase.from).toHaveBeenCalledWith('core_events');
320
- expect(freshMockQueryBuilder.delete).toHaveBeenCalled();
321
- expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('id', 'event-123');
322
- expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
323
- });
324
-
325
- it('handles delete errors gracefully', async () => {
326
- // Create a query builder that rejects
327
- const errorBuilder = createMockQueryBuilderWithData([], new Error('Delete failed'));
328
- freshMockSupabase.from.mockReturnValue(errorBuilder);
329
-
330
- const { result } = renderHook(() => useSecureDataAccess());
331
-
332
- await expect(result.current.secureDelete('users', { id: 'user-123' })).rejects.toThrow('Delete failed');
333
- });
334
- });
335
-
336
- describe('Secure RPC Operations', () => {
337
- it('executes secure RPC with organisation context', async () => {
338
- const { result } = renderHook(() => useSecureDataAccess());
339
-
340
- const data = await result.current.secureRpc('get_user_data', { user_id: 'user-123' });
341
-
342
- expect(freshMockSupabase.rpc).toHaveBeenCalledWith('get_user_data', {
343
- user_id: 'user-123',
344
- p_user_id: 'user-123',
345
- organisation_id: 'org-123'
346
- });
347
- });
348
-
349
- it('handles RPC errors gracefully', async () => {
350
- // Mock error response - the RPC should reject with an error
351
- freshMockSupabase.rpc.mockRejectedValue(new Error('RPC failed'));
352
-
353
- const { result } = renderHook(() => useSecureDataAccess());
354
-
355
- await expect(result.current.secureRpc('get_user_data')).rejects.toThrow('RPC failed');
356
- });
357
- });
358
-
359
- describe('Organisation Context Management', () => {
360
- it('sets organisation context before operations', async () => {
361
- const { result } = renderHook(() => useSecureDataAccess());
362
-
363
- await result.current.secureQuery('users', '*');
364
-
365
- expect(mockSetOrganisationContext).toHaveBeenCalledWith(freshMockSupabase, 'org-123');
366
- });
367
-
368
- it('handles missing organisation context', () => {
369
- mockUseUnifiedAuth.mockReturnValue({
370
- user: mockUser,
371
- session: mockSession,
372
- supabase: freshMockSupabase,
373
- isAuthenticated: true,
374
- signOut: vi.fn(),
375
- selectedOrganisation: null,
376
- selectedEvent: null,
377
- } as any);
378
-
379
- // Mock useResolvedScope to return null scope when no context available
380
- mockUseResolvedScope.mockReturnValue({
381
- resolvedScope: null,
382
- isLoading: false,
383
- error: null,
384
- });
385
-
386
- const { result } = renderHook(() => useSecureDataAccess());
387
-
388
- expect(() => result.current.getCurrentOrganisationId()).toThrow('Organisation context is required for data access');
389
- });
390
-
391
- it('validates organisation access before operations', async () => {
392
- // The hook uses useResolvedScope to get organisationId, which is already mocked in beforeEach
393
- // The validation happens in validateContext which checks resolvedScope.organisationId
394
- const { result } = renderHook(() => useSecureDataAccess());
395
-
396
- // Use 'event' table which is in the tablesWithOrganisation list
397
- await result.current.secureQuery('core_events', '*');
398
-
399
- // Verify that the query was executed with organisation filter
400
- expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
401
- });
402
- });
403
-
404
- describe('Error Handling', () => {
405
- it('handles missing user context', () => {
406
- mockUseUnifiedAuth.mockReturnValue({
407
- user: null,
408
- session: null,
409
- supabase: null,
410
- isAuthenticated: false,
411
- signOut: vi.fn(),
412
- // Add other required properties
413
- } as any);
414
-
415
- const { result } = renderHook(() => useSecureDataAccess());
416
-
417
- expect(() => result.current.getCurrentOrganisationId()).toThrow('No Supabase client available');
418
- });
419
-
420
- it('handles missing supabase client', () => {
421
- mockUseUnifiedAuth.mockReturnValue({
422
- user: mockUser,
423
- session: mockSession,
424
- supabase: null,
425
- isAuthenticated: true,
426
- signOut: vi.fn(),
427
- // Add other required properties
428
- } as any);
429
-
430
- const { result } = renderHook(() => useSecureDataAccess());
431
-
432
- expect(() => result.current.getCurrentOrganisationId()).toThrow('No Supabase client available');
433
- });
434
-
435
- it('handles organisation access validation failures', async () => {
436
- // Mock useResolvedScope to return null scope to simulate missing context
437
- mockUseResolvedScope.mockReturnValue({
438
- resolvedScope: null,
439
- isLoading: false,
440
- error: null,
441
- });
442
-
443
- const { result } = renderHook(() => useSecureDataAccess());
444
-
445
- await expect(result.current.secureQuery('users', '*')).rejects.toThrow('Organisation context is required for data access');
446
- });
447
- });
448
-
449
- describe('Security Features', () => {
450
- it('prevents data leaks between organisations', async () => {
451
- const { result } = renderHook(() => useSecureDataAccess());
452
-
453
- await result.current.secureQuery('core_events', '*');
454
-
455
- // Verify organisation_id filter is always applied
456
- expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
457
- });
458
-
459
- it('prevents organisation_id manipulation in inserts', async () => {
460
- const { result } = renderHook(() => useSecureDataAccess());
461
-
462
- await result.current.secureInsert('users', {
463
- name: 'Test User',
464
- organisation_id: 'malicious-org-id'
465
- });
466
-
467
- expect(freshMockQueryBuilder.insert).toHaveBeenCalledWith({
468
- name: 'Test User',
469
- organisation_id: 'org-123' // Should override malicious value
470
- });
471
- });
472
-
473
- it('prevents organisation_id manipulation in updates', async () => {
474
- const { result } = renderHook(() => useSecureDataAccess());
475
-
476
- await result.current.secureUpdate('users', {
477
- name: 'Updated User',
478
- organisation_id: 'malicious-org-id'
479
- }, { id: 'user-123' });
480
-
481
- expect(freshMockQueryBuilder.update).toHaveBeenCalledWith({
482
- name: 'Updated User'
483
- // organisation_id should be filtered out
484
- });
485
- });
486
- });
487
-
488
- describe('Performance', () => {
489
- it('memoizes results to prevent unnecessary re-renders', () => {
490
- const { result, rerender } = renderHook(() => useSecureDataAccess());
491
- const initialSecureQuery = result.current.secureQuery;
492
- const initialGetCurrentOrganisationId = result.current.getCurrentOrganisationId;
493
-
494
- // Re-render with same props
495
- rerender();
496
-
497
- // Should not cause additional renders due to memoization
498
- expect(result.current.secureQuery).toBe(initialSecureQuery);
499
- expect(result.current.getCurrentOrganisationId).toBe(initialGetCurrentOrganisationId);
500
- });
501
-
502
- it('handles organisation changes efficiently', () => {
503
- const { result, rerender } = renderHook(() => useSecureDataAccess());
504
-
505
- expect(result.current.getCurrentOrganisationId()).toBe('org-123');
506
-
507
- // Change organisation
508
- const newOrganisation = { id: 'org-456', name: 'New Organisation' };
509
- mockUseUnifiedAuth.mockReturnValue({
510
- user: mockUser,
511
- session: mockSession,
512
- supabase: freshMockSupabase,
513
- isAuthenticated: true,
514
- signOut: vi.fn(),
515
- selectedOrganisation: newOrganisation,
516
- } as any);
517
-
518
- // Update useResolvedScope mock to return new organisation
519
- mockUseResolvedScope.mockReturnValue({
520
- resolvedScope: {
521
- organisationId: 'org-456',
522
- eventId: undefined,
523
- appId: undefined,
524
- },
525
- isLoading: false,
526
- error: null,
527
- });
528
-
529
- rerender();
530
-
531
- expect(result.current.getCurrentOrganisationId()).toBe('org-456');
532
- });
533
- });
534
-
535
- describe('Integration with Providers', () => {
536
- it('integrates with useUnifiedAuth hook correctly', () => {
537
- renderHook(() => useSecureDataAccess());
538
- expect(mockUseUnifiedAuth).toHaveBeenCalled();
539
- });
540
-
541
- it('integrates with useResolvedScope hook correctly', () => {
542
- renderHook(() => useSecureDataAccess());
543
- // useSecureDataAccess now uses useResolvedScope instead of useOrganisations
544
- expect(mockUseResolvedScope).toHaveBeenCalled();
545
- });
546
-
547
- it('uses organisation context from provider', () => {
548
- const { result } = renderHook(() => useSecureDataAccess());
549
-
550
- expect(result.current.getCurrentOrganisationId()).toBe('org-123');
551
- });
552
-
553
- it('validates organisation access through provider', () => {
554
- // This test is removed as validateOrganisationAccess is not used in useSecureDataAccess
555
- // The hook only calls ensureOrganisationContext during operations
556
- expect(true).toBe(true);
557
- });
558
- });
559
- });