@jmruthers/pace-core 0.6.1 → 0.6.3

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 (549) hide show
  1. package/CHANGELOG.md +88 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +46 -87
  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-Cb34EQs3.d.ts} +63 -1
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-THFPBKTP.js} +12 -10
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DEMpysFR.d.ts} +394 -171
  17. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +30 -8
  18. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
  19. package/dist/{api-N774RPUA.js → api-IAGWF3ZG.js} +10 -10
  20. package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
  21. package/dist/{chunk-JBKQ3SAO.js → chunk-2T2IG7T7.js} +107 -57
  22. package/dist/chunk-2T2IG7T7.js.map +1 -0
  23. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  24. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  25. package/dist/{chunk-3XTALGJF.js → chunk-6Z7LTB3D.js} +69 -240
  26. package/dist/chunk-6Z7LTB3D.js.map +1 -0
  27. package/dist/{chunk-4ZC4GX36.js → chunk-CNCQDFLN.js} +199 -46
  28. package/dist/chunk-CNCQDFLN.js.map +1 -0
  29. package/dist/chunk-DGUM43GV.js +11 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-DWUBLJJM.js} +361 -187
  31. package/dist/chunk-DWUBLJJM.js.map +1 -0
  32. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  33. package/dist/chunk-FFQEQTNW.js.map +1 -0
  34. package/dist/chunk-FMUCXFII.js +76 -0
  35. package/dist/chunk-FMUCXFII.js.map +1 -0
  36. package/dist/{chunk-4N5C5XZU.js → chunk-HFZBI76P.js} +4 -4
  37. package/dist/chunk-HFZBI76P.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-I7PSE6JW.js → chunk-M7MPQISP.js} +3 -76
  43. package/dist/chunk-M7MPQISP.js.map +1 -0
  44. package/dist/chunk-PQBSKX33.js +7793 -0
  45. package/dist/chunk-PQBSKX33.js.map +1 -0
  46. package/dist/chunk-QRPVRXYT.js +226 -0
  47. package/dist/chunk-QRPVRXYT.js.map +1 -0
  48. package/dist/{chunk-KNC55RTG.js → chunk-RWEBCB47.js} +194 -416
  49. package/dist/chunk-RWEBCB47.js.map +1 -0
  50. package/dist/{chunk-XM25TVIE.js → chunk-YDQHOZNA.js} +843 -388
  51. package/dist/chunk-YDQHOZNA.js.map +1 -0
  52. package/dist/{chunk-GLK6VM3F.js → chunk-ZNIWI3UC.js} +739 -737
  53. package/dist/chunk-ZNIWI3UC.js.map +1 -0
  54. package/dist/components.d.ts +5 -5
  55. package/dist/components.js +18 -16
  56. package/dist/components.js.map +1 -1
  57. package/dist/contextValidator-3JNZKUTX.js +9 -0
  58. package/dist/contextValidator-3JNZKUTX.js.map +1 -0
  59. package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
  60. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  61. package/dist/hooks.d.ts +55 -122
  62. package/dist/hooks.js +10 -13
  63. package/dist/hooks.js.map +1 -1
  64. package/dist/index.d.ts +60 -13
  65. package/dist/index.js +30 -25
  66. package/dist/index.js.map +1 -1
  67. package/dist/providers.d.ts +21 -3
  68. package/dist/providers.js +4 -3
  69. package/dist/rbac/index.d.ts +210 -139
  70. package/dist/rbac/index.js +17 -13
  71. package/dist/styles/index.js +1 -1
  72. package/dist/theming/runtime.d.ts +1 -13
  73. package/dist/theming/runtime.js +2 -2
  74. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  75. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  76. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  77. package/dist/types.d.ts +2 -2
  78. package/dist/types.js +1 -1
  79. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +38 -17
  80. package/dist/utils.d.ts +4 -5
  81. package/dist/utils.js +17 -19
  82. package/dist/utils.js.map +1 -1
  83. package/docs/api/README.md +21 -17
  84. package/docs/api/modules.md +4191 -2967
  85. package/docs/architecture/database-schema-requirements.md +161 -0
  86. package/docs/components/context-selector.md +126 -0
  87. package/docs/core-concepts/rbac-system.md +3 -3
  88. package/docs/documentation-index.md +2 -4
  89. package/docs/getting-started/cursor-rules.md +2 -1
  90. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  91. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  92. package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
  93. package/docs/migration/README.md +52 -6
  94. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  95. package/docs/migration/database-changes-december-2025.md +3 -3
  96. package/docs/pace-mint-fix-auto-selection.md +218 -0
  97. package/docs/pace-mint-rbac-setup.md +391 -0
  98. package/docs/rbac/event-based-apps.md +1 -1
  99. package/docs/rbac/getting-started.md +1 -1
  100. package/docs/rbac/quick-start.md +1 -1
  101. package/docs/rbac/secure-client-protection.md +330 -0
  102. package/docs/standards/README.md +1 -0
  103. package/package.json +4 -3
  104. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  105. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  106. package/scripts/audit/core/checks/bundle.cjs +142 -0
  107. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +784 -685
  108. package/scripts/audit/core/checks/config.cjs +54 -0
  109. package/scripts/audit/core/checks/coverage.cjs +84 -0
  110. package/scripts/audit/core/checks/dependencies.cjs +985 -0
  111. package/scripts/audit/core/checks/documentation.cjs +268 -0
  112. package/scripts/audit/core/checks/environment.cjs +116 -0
  113. package/scripts/audit/core/checks/error-handling.cjs +340 -0
  114. package/scripts/audit/core/checks/forms.cjs +172 -0
  115. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  116. package/scripts/audit/core/checks/hooks.cjs +334 -0
  117. package/scripts/audit/core/checks/imports.cjs +244 -0
  118. package/scripts/audit/core/checks/performance.cjs +325 -0
  119. package/scripts/audit/core/checks/routes.cjs +117 -0
  120. package/scripts/audit/core/checks/state.cjs +130 -0
  121. package/scripts/audit/core/checks/structure.cjs +65 -0
  122. package/scripts/audit/core/checks/style.cjs +584 -0
  123. package/scripts/audit/core/checks/testing.cjs +122 -0
  124. package/scripts/audit/core/checks/typescript.cjs +61 -0
  125. package/scripts/audit/core/scanner.cjs +199 -0
  126. package/scripts/audit/core/utils.cjs +137 -0
  127. package/scripts/audit/index.cjs +223 -0
  128. package/scripts/audit/reporters/console.cjs +151 -0
  129. package/scripts/audit/reporters/json.cjs +54 -0
  130. package/scripts/audit/reporters/markdown.cjs +124 -0
  131. package/scripts/audit-consuming-app.cjs +61 -936
  132. package/scripts/build-docs/build-decision.js +240 -0
  133. package/scripts/build-docs/cache-utils.js +105 -0
  134. package/scripts/build-docs/content-normalization.js +150 -0
  135. package/scripts/build-docs/file-utils.js +105 -0
  136. package/scripts/build-docs/git-utils.js +86 -0
  137. package/scripts/build-docs/hash-utils.js +116 -0
  138. package/scripts/build-docs/typedoc-runner.js +220 -0
  139. package/scripts/build-docs-incremental.js +77 -913
  140. package/scripts/utils/command-runner.js +16 -11
  141. package/scripts/validate-formats.js +61 -56
  142. package/scripts/validate-master.js +74 -69
  143. package/scripts/validate-pre-publish.js +70 -65
  144. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  145. package/src/components/Alert/Alert.test.tsx +12 -18
  146. package/src/components/Alert/Alert.tsx +5 -7
  147. package/src/components/Avatar/Avatar.test.tsx +4 -4
  148. package/src/components/Badge/Badge.tsx +14 -0
  149. package/src/components/Button/Button.tsx +22 -0
  150. package/src/components/Calendar/Calendar.tsx +8 -2
  151. package/src/components/Card/Card.tsx +4 -0
  152. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  153. package/src/components/Checkbox/Checkbox.tsx +2 -2
  154. package/src/components/ContextSelector/ContextSelector.tsx +384 -0
  155. package/src/components/ContextSelector/index.ts +3 -0
  156. package/src/components/DataTable/DataTable.tsx +38 -4
  157. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  158. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  159. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  160. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  161. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  162. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  163. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  164. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  165. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  166. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  167. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  168. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  169. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  170. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  171. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  172. package/src/components/DataTable/components/EditFields.tsx +307 -0
  173. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  174. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  175. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  176. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  177. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  178. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  179. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  180. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  181. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  182. package/src/components/DataTable/components/UnifiedTableBody.tsx +63 -851
  183. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  184. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  185. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  186. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  187. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  188. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  189. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  190. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  191. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  192. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  193. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  194. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  195. package/src/components/DataTable/hooks/useDataTablePermissions.ts +127 -33
  196. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  197. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  198. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  199. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  200. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  201. package/src/components/DataTable/styles.ts +6 -6
  202. package/src/components/DataTable/types.ts +6 -10
  203. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  204. package/src/components/DataTable/utils/debugTools.ts +18 -113
  205. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  206. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  207. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  208. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  209. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  210. package/src/components/Dialog/Dialog.tsx +31 -3
  211. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  212. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  213. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  214. package/src/components/ErrorBoundary/index.ts +27 -2
  215. package/src/components/FileDisplay/FileDisplay.tsx +74 -28
  216. package/src/components/FileUpload/FileUpload.tsx +22 -2
  217. package/src/components/Footer/Footer.test.tsx +16 -16
  218. package/src/components/Footer/Footer.tsx +14 -11
  219. package/src/components/Form/Form.tsx +1 -0
  220. package/src/components/Header/Header.test.tsx +43 -73
  221. package/src/components/Header/Header.tsx +59 -49
  222. package/src/components/Input/Input.test.tsx +2 -2
  223. package/src/components/Input/Input.tsx +8 -4
  224. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  225. package/src/components/LoginForm/LoginForm.tsx +4 -0
  226. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  227. package/src/components/NavigationMenu/types.ts +56 -0
  228. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  229. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
  230. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
  231. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
  232. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +13 -11
  233. package/src/components/PaceAppLayout/PaceAppLayout.tsx +167 -44
  234. package/src/components/PaceAppLayout/README.md +14 -17
  235. package/src/components/PaceAppLayout/test-setup.tsx +3 -4
  236. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  237. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  238. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  239. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  240. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  241. package/src/components/Select/Select.tsx +80 -434
  242. package/src/components/Select/context.ts +23 -0
  243. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  244. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  245. package/src/components/Select/hooks/useSelectState.ts +104 -0
  246. package/src/components/Select/index.ts +9 -1
  247. package/src/components/Select/types.ts +123 -0
  248. package/src/components/Select/utils/text.ts +26 -0
  249. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  250. package/src/components/Switch/Switch.tsx +4 -4
  251. package/src/components/Tabs/Tabs.tsx +1 -1
  252. package/src/components/Toast/Toast.tsx +4 -0
  253. package/src/components/Tooltip/Tooltip.tsx +2 -2
  254. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  255. package/src/components/UserMenu/UserMenu.tsx +21 -18
  256. package/src/components/index.ts +7 -7
  257. package/src/eslint-rules/pace-core-compliance.cjs +106 -0
  258. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  259. package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
  260. package/src/hooks/index.ts +1 -2
  261. package/src/hooks/public/usePublicEvent.ts +4 -0
  262. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  263. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  264. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  265. package/src/hooks/services/useAuth.ts +32 -0
  266. package/src/hooks/services/useCurrentEvent.ts +6 -0
  267. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  268. package/src/hooks/useAppConfig.ts +15 -30
  269. package/src/hooks/useDebounce.ts +9 -0
  270. package/src/hooks/useEventTheme.ts +6 -0
  271. package/src/hooks/useFileDisplay.ts +81 -50
  272. package/src/hooks/useFileReference.ts +25 -7
  273. package/src/hooks/useFileUrl.ts +11 -1
  274. package/src/hooks/useFocusManagement.ts +14 -0
  275. package/src/hooks/useFocusTrap.ts +3 -0
  276. package/src/hooks/useInactivityTracker.ts +3 -0
  277. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  278. package/src/hooks/useOrganisationPermissions.ts +4 -0
  279. package/src/hooks/useOrganisationSecurity.ts +4 -0
  280. package/src/hooks/usePerformanceMonitor.ts +4 -0
  281. package/src/hooks/usePermissionCache.ts +7 -0
  282. package/src/hooks/useQueryCache.ts +12 -1
  283. package/src/hooks/useSessionRestoration.ts +4 -0
  284. package/src/hooks/useStorage.ts +4 -0
  285. package/src/hooks/useToast.ts +1 -1
  286. package/src/index.ts +6 -6
  287. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  288. package/src/providers/services/AuthServiceProvider.tsx +35 -7
  289. package/src/providers/services/EventServiceProvider.tsx +51 -5
  290. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  291. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  292. package/src/providers/services/UnifiedAuthProvider.tsx +126 -134
  293. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  294. package/src/rbac/README.md +1 -1
  295. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
  296. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  297. package/src/rbac/adapters.tsx +12 -3
  298. package/src/rbac/api.test.ts +59 -51
  299. package/src/rbac/api.ts +246 -167
  300. package/src/rbac/components/NavigationProvider.tsx +4 -1
  301. package/src/rbac/components/PagePermissionGuard.tsx +185 -17
  302. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  303. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  304. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  305. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  306. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  307. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  308. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  309. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  310. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  311. package/src/rbac/engine.ts +38 -14
  312. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
  313. package/src/rbac/hooks/permissions/index.ts +7 -0
  314. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  315. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  316. package/src/rbac/hooks/permissions/useCan.ts +377 -0
  317. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  318. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  319. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  320. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  321. package/src/rbac/hooks/useCan.test.ts +64 -66
  322. package/src/rbac/hooks/usePermissions.ts +14 -995
  323. package/src/rbac/hooks/useRBAC.test.ts +1 -5
  324. package/src/rbac/hooks/useRBAC.ts +36 -37
  325. package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
  326. package/src/rbac/hooks/useResolvedScope.ts +35 -40
  327. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  328. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  329. package/src/rbac/hooks/useSecureSupabase.ts +27 -7
  330. package/src/rbac/index.ts +7 -0
  331. package/src/rbac/permissions.ts +0 -30
  332. package/src/rbac/secureClient.test.ts +22 -18
  333. package/src/rbac/secureClient.ts +294 -68
  334. package/src/rbac/security.ts +0 -17
  335. package/src/rbac/types.ts +9 -0
  336. package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
  337. package/src/rbac/utils/clientSecurity.ts +93 -0
  338. package/src/rbac/utils/contextValidator.ts +77 -168
  339. package/src/services/AuthService.ts +39 -7
  340. package/src/services/EventService.ts +186 -54
  341. package/src/services/OrganisationService.ts +81 -14
  342. package/src/services/__tests__/EventService.test.ts +1 -2
  343. package/src/services/base/BaseService.ts +3 -0
  344. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  345. package/src/theming/parseEventColours.ts +5 -19
  346. package/src/types/vitest-globals.d.ts +51 -26
  347. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  348. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  349. package/src/utils/__tests__/index.unit.test.ts +2 -2
  350. package/src/utils/audit/audit.ts +0 -3
  351. package/src/utils/core/cn.ts +1 -1
  352. package/src/utils/dynamic/dynamicUtils.ts +7 -4
  353. package/src/utils/file-reference/index.ts +53 -1
  354. package/src/utils/formatting/formatting.ts +8 -18
  355. package/src/utils/index.ts +0 -1
  356. package/dist/chunk-3QRJFVBR.js.map +0 -1
  357. package/dist/chunk-3XTALGJF.js.map +0 -1
  358. package/dist/chunk-4N5C5XZU.js.map +0 -1
  359. package/dist/chunk-4ZC4GX36.js.map +0 -1
  360. package/dist/chunk-7D4SUZUM.js +0 -38
  361. package/dist/chunk-BYFSK72L.js.map +0 -1
  362. package/dist/chunk-EXUD6RNJ.js +0 -451
  363. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  364. package/dist/chunk-GLK6VM3F.js.map +0 -1
  365. package/dist/chunk-I7PSE6JW.js.map +0 -1
  366. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  367. package/dist/chunk-KNC55RTG.js.map +0 -1
  368. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  369. package/dist/chunk-R77UEZ4E.js.map +0 -1
  370. package/dist/chunk-SQGMNID3.js.map +0 -1
  371. package/dist/chunk-T33XF5ZC.js +0 -12922
  372. package/dist/chunk-T33XF5ZC.js.map +0 -1
  373. package/dist/chunk-XM25TVIE.js.map +0 -1
  374. package/docs/api/classes/ColumnFactory.md +0 -243
  375. package/docs/api/classes/ErrorBoundary.md +0 -144
  376. package/docs/api/classes/InvalidScopeError.md +0 -73
  377. package/docs/api/classes/Logger.md +0 -178
  378. package/docs/api/classes/MissingUserContextError.md +0 -66
  379. package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
  380. package/docs/api/classes/PermissionDeniedError.md +0 -73
  381. package/docs/api/classes/RBACAuditManager.md +0 -297
  382. package/docs/api/classes/RBACCache.md +0 -322
  383. package/docs/api/classes/RBACEngine.md +0 -171
  384. package/docs/api/classes/RBACError.md +0 -76
  385. package/docs/api/classes/RBACNotInitializedError.md +0 -66
  386. package/docs/api/classes/SecureSupabaseClient.md +0 -160
  387. package/docs/api/classes/StorageUtils.md +0 -328
  388. package/docs/api/enums/FileCategory.md +0 -184
  389. package/docs/api/enums/LogLevel.md +0 -54
  390. package/docs/api/enums/RBACErrorCode.md +0 -228
  391. package/docs/api/enums/RPCFunction.md +0 -118
  392. package/docs/api/interfaces/AddressFieldProps.md +0 -241
  393. package/docs/api/interfaces/AddressFieldRef.md +0 -94
  394. package/docs/api/interfaces/AggregateConfig.md +0 -43
  395. package/docs/api/interfaces/AutocompleteOptions.md +0 -75
  396. package/docs/api/interfaces/AvatarProps.md +0 -128
  397. package/docs/api/interfaces/BadgeProps.md +0 -27
  398. package/docs/api/interfaces/ButtonProps.md +0 -53
  399. package/docs/api/interfaces/CalendarProps.md +0 -70
  400. package/docs/api/interfaces/CardProps.md +0 -66
  401. package/docs/api/interfaces/ColorPalette.md +0 -7
  402. package/docs/api/interfaces/ColorShade.md +0 -66
  403. package/docs/api/interfaces/ComplianceResult.md +0 -30
  404. package/docs/api/interfaces/DataAccessRecord.md +0 -96
  405. package/docs/api/interfaces/DataRecord.md +0 -11
  406. package/docs/api/interfaces/DataTableAction.md +0 -249
  407. package/docs/api/interfaces/DataTableColumn.md +0 -504
  408. package/docs/api/interfaces/DataTableProps.md +0 -625
  409. package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
  410. package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
  411. package/docs/api/interfaces/DatabaseIssue.md +0 -41
  412. package/docs/api/interfaces/EmptyStateConfig.md +0 -61
  413. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
  414. package/docs/api/interfaces/EventAppRoleData.md +0 -71
  415. package/docs/api/interfaces/ExportColumn.md +0 -90
  416. package/docs/api/interfaces/ExportOptions.md +0 -126
  417. package/docs/api/interfaces/FileDisplayProps.md +0 -249
  418. package/docs/api/interfaces/FileMetadata.md +0 -129
  419. package/docs/api/interfaces/FileReference.md +0 -118
  420. package/docs/api/interfaces/FileSizeLimits.md +0 -7
  421. package/docs/api/interfaces/FileUploadOptions.md +0 -139
  422. package/docs/api/interfaces/FileUploadProps.md +0 -293
  423. package/docs/api/interfaces/FooterProps.md +0 -105
  424. package/docs/api/interfaces/FormFieldProps.md +0 -166
  425. package/docs/api/interfaces/FormProps.md +0 -113
  426. package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
  427. package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
  428. package/docs/api/interfaces/InputProps.md +0 -53
  429. package/docs/api/interfaces/LabelProps.md +0 -107
  430. package/docs/api/interfaces/LoggerConfig.md +0 -62
  431. package/docs/api/interfaces/LoginFormProps.md +0 -184
  432. package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
  433. package/docs/api/interfaces/NavigationContextType.md +0 -164
  434. package/docs/api/interfaces/NavigationGuardProps.md +0 -139
  435. package/docs/api/interfaces/NavigationItem.md +0 -120
  436. package/docs/api/interfaces/NavigationMenuProps.md +0 -221
  437. package/docs/api/interfaces/NavigationProviderProps.md +0 -117
  438. package/docs/api/interfaces/Organisation.md +0 -140
  439. package/docs/api/interfaces/OrganisationContextType.md +0 -388
  440. package/docs/api/interfaces/OrganisationMembership.md +0 -140
  441. package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
  442. package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
  443. package/docs/api/interfaces/PaceAppLayoutProps.md +0 -406
  444. package/docs/api/interfaces/PaceLoginPageProps.md +0 -47
  445. package/docs/api/interfaces/PageAccessRecord.md +0 -85
  446. package/docs/api/interfaces/PagePermissionContextType.md +0 -140
  447. package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
  448. package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
  449. package/docs/api/interfaces/PaletteData.md +0 -41
  450. package/docs/api/interfaces/ParsedAddress.md +0 -120
  451. package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
  452. package/docs/api/interfaces/ProgressProps.md +0 -42
  453. package/docs/api/interfaces/ProtectedRouteProps.md +0 -97
  454. package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
  455. package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
  456. package/docs/api/interfaces/PublicPageLayoutProps.md +0 -198
  457. package/docs/api/interfaces/QuickFix.md +0 -52
  458. package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
  459. package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
  460. package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
  461. package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
  462. package/docs/api/interfaces/RBACConfig.md +0 -133
  463. package/docs/api/interfaces/RBACContext.md +0 -52
  464. package/docs/api/interfaces/RBACLogger.md +0 -112
  465. package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
  466. package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
  467. package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
  468. package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
  469. package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
  470. package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
  471. package/docs/api/interfaces/RBACResult.md +0 -58
  472. package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
  473. package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
  474. package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
  475. package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
  476. package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
  477. package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
  478. package/docs/api/interfaces/RBACRolesListParams.md +0 -52
  479. package/docs/api/interfaces/RBACRolesListResult.md +0 -74
  480. package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
  481. package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
  482. package/docs/api/interfaces/ResourcePermissions.md +0 -155
  483. package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
  484. package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
  485. package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
  486. package/docs/api/interfaces/RoleManagementResult.md +0 -52
  487. package/docs/api/interfaces/RouteAccessRecord.md +0 -107
  488. package/docs/api/interfaces/RouteConfig.md +0 -134
  489. package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
  490. package/docs/api/interfaces/SecureDataContextType.md +0 -168
  491. package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
  492. package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
  493. package/docs/api/interfaces/SetupIssue.md +0 -41
  494. package/docs/api/interfaces/StorageConfig.md +0 -41
  495. package/docs/api/interfaces/StorageFileInfo.md +0 -74
  496. package/docs/api/interfaces/StorageFileMetadata.md +0 -151
  497. package/docs/api/interfaces/StorageListOptions.md +0 -99
  498. package/docs/api/interfaces/StorageListResult.md +0 -41
  499. package/docs/api/interfaces/StorageUploadOptions.md +0 -101
  500. package/docs/api/interfaces/StorageUploadResult.md +0 -63
  501. package/docs/api/interfaces/StorageUrlOptions.md +0 -60
  502. package/docs/api/interfaces/StyleImport.md +0 -19
  503. package/docs/api/interfaces/SwitchProps.md +0 -34
  504. package/docs/api/interfaces/TabsContentProps.md +0 -9
  505. package/docs/api/interfaces/TabsListProps.md +0 -9
  506. package/docs/api/interfaces/TabsProps.md +0 -9
  507. package/docs/api/interfaces/TabsTriggerProps.md +0 -50
  508. package/docs/api/interfaces/TextareaProps.md +0 -53
  509. package/docs/api/interfaces/ToastActionElement.md +0 -9
  510. package/docs/api/interfaces/ToastProps.md +0 -9
  511. package/docs/api/interfaces/UnifiedAuthContextType.md +0 -820
  512. package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -171
  513. package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
  514. package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
  515. package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -136
  516. package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
  517. package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
  518. package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
  519. package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
  520. package/docs/api/interfaces/UsePublicEventReturn.md +0 -68
  521. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
  522. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -120
  523. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -94
  524. package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
  525. package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
  526. package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
  527. package/docs/api/interfaces/UserEventAccess.md +0 -118
  528. package/docs/api/interfaces/UserMenuProps.md +0 -86
  529. package/docs/api/interfaces/UserProfile.md +0 -63
  530. package/docs/migration/quick-migration-guide.md +0 -356
  531. package/docs/migration/service-architecture.md +0 -281
  532. package/src/components/EventSelector/EventSelector.test.tsx +0 -720
  533. package/src/components/EventSelector/EventSelector.tsx +0 -420
  534. package/src/components/EventSelector/index.ts +0 -3
  535. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
  536. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -324
  537. package/src/components/OrganisationSelector/index.ts +0 -9
  538. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  539. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  540. package/src/hooks/useSecureDataAccess.ts +0 -681
  541. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-THFPBKTP.js.map} +0 -0
  542. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
  543. /package/dist/{api-N774RPUA.js.map → api-IAGWF3ZG.js.map} +0 -0
  544. /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
  545. /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
  546. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  547. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  548. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  549. /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
- });