@jmruthers/pace-core 0.5.54 → 0.5.55

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 (396) hide show
  1. package/README.md +0 -4
  2. package/dist/{DataTable-7FMFXA7A.js → DataTable-4T627QFJ.js} +11 -11
  3. package/dist/{PublicLoadingSpinner-Bq_-BeK-.d.ts → PublicLoadingSpinner-SL8WaQN7.d.ts} +2 -21
  4. package/dist/{api-H5A3H4IR.js → api-LUNF5O6M.js} +3 -3
  5. package/dist/{appConfig-BVGyuvI7.d.ts → appConfig-DjpeG6P-.d.ts} +9 -1
  6. package/dist/{appNameResolver-7GHF5ED2.js → appNameResolver-UURKN7NF.js} +2 -2
  7. package/dist/{audit-BUW3LMJB.js → audit-6TOCAMKO.js} +2 -2
  8. package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
  9. package/dist/chunk-5BSLGBYI.js.map +1 -0
  10. package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
  11. package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
  12. package/dist/{chunk-MYP2EGHX.js → chunk-AJ2KMES7.js} +21 -14
  13. package/dist/chunk-AJ2KMES7.js.map +1 -0
  14. package/dist/{chunk-EL2O4IUX.js → chunk-AQFRLC7K.js} +16 -24
  15. package/dist/{chunk-EL2O4IUX.js.map → chunk-AQFRLC7K.js.map} +1 -1
  16. package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
  17. package/dist/chunk-B2WTCLCV.js.map +1 -0
  18. package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
  19. package/dist/chunk-D7ARGIA3.js.map +1 -0
  20. package/dist/{chunk-NRK4AIHQ.js → chunk-KBRACSJI.js} +3 -3
  21. package/dist/{chunk-NYUJ4FJR.js → chunk-KJDPSM64.js} +7 -7
  22. package/dist/chunk-KJDPSM64.js.map +1 -0
  23. package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
  24. package/dist/chunk-KLPVOPRI.js.map +1 -0
  25. package/dist/{chunk-TRIZ7IB7.js → chunk-MPQDF75X.js} +148 -288
  26. package/dist/chunk-MPQDF75X.js.map +1 -0
  27. package/dist/{chunk-MSFACPQQ.js → chunk-PAEM3OWN.js} +11 -11
  28. package/dist/{chunk-MSFACPQQ.js.map → chunk-PAEM3OWN.js.map} +1 -1
  29. package/dist/{chunk-GIO7BFE7.js → chunk-RQD3D2CO.js} +66 -169
  30. package/dist/{chunk-GIO7BFE7.js.map → chunk-RQD3D2CO.js.map} +1 -1
  31. package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
  32. package/dist/chunk-STT7INZR.js.map +1 -0
  33. package/dist/{chunk-6MTY77WU.js → chunk-TNMXZLDR.js} +3 -3
  34. package/dist/{chunk-BC3S53OZ.js → chunk-UQE2Y64H.js} +30 -14
  35. package/dist/chunk-UQE2Y64H.js.map +1 -0
  36. package/dist/{chunk-22KLBHPS.js → chunk-W66AZIOH.js} +2 -2
  37. package/dist/chunk-W66AZIOH.js.map +1 -0
  38. package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
  39. package/dist/chunk-YNUBMSMV.js.map +1 -0
  40. package/dist/{chunk-NZ655MWE.js → chunk-ZOD2ZY6X.js} +5 -4
  41. package/dist/chunk-ZOD2ZY6X.js.map +1 -0
  42. package/dist/{chunk-74C6SNEC.js → chunk-ZPK5656W.js} +3 -3
  43. package/dist/{chunk-74C6SNEC.js.map → chunk-ZPK5656W.js.map} +1 -1
  44. package/dist/components.d.ts +22 -899
  45. package/dist/components.js +436 -3118
  46. package/dist/components.js.map +1 -1
  47. package/dist/file-reference-9xUOnwyt.d.ts +70 -0
  48. package/dist/hooks.d.ts +2 -2
  49. package/dist/hooks.js +10 -10
  50. package/dist/hooks.js.map +1 -1
  51. package/dist/index.d.ts +49 -9
  52. package/dist/index.js +190 -25
  53. package/dist/index.js.map +1 -1
  54. package/dist/{organisation-CO3Sh3_D.d.ts → organisation-t-vvQC3g.d.ts} +1 -8
  55. package/dist/providers.d.ts +2 -2
  56. package/dist/providers.js +5 -5
  57. package/dist/rbac/index.d.ts +65 -46
  58. package/dist/rbac/index.js +10 -12
  59. package/dist/styles/core.css +0 -125
  60. package/dist/types.d.ts +2 -1
  61. package/dist/types.js +3 -1
  62. package/dist/types.js.map +1 -1
  63. package/dist/{usePublicRouteParams-B2OcAsur.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +1 -1
  64. package/dist/utils.d.ts +3 -4
  65. package/dist/utils.js +44 -13
  66. package/dist/utils.js.map +1 -1
  67. package/docs/FILE_REFERENCE_SYSTEM.md +440 -0
  68. package/docs/INDEX.md +7 -5
  69. package/docs/README.md +0 -1
  70. package/docs/api/README.md +0 -4
  71. package/docs/api/classes/ErrorBoundary.md +1 -1
  72. package/docs/api/classes/InvalidScopeError.md +1 -1
  73. package/docs/api/classes/MissingUserContextError.md +1 -1
  74. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  75. package/docs/api/classes/PermissionDeniedError.md +2 -2
  76. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  77. package/docs/api/classes/RBACAuditManager.md +12 -12
  78. package/docs/api/classes/RBACCache.md +1 -1
  79. package/docs/api/classes/RBACEngine.md +6 -6
  80. package/docs/api/classes/RBACError.md +1 -1
  81. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  82. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  83. package/docs/api/classes/StorageUtils.md +281 -0
  84. package/docs/api/interfaces/AggregateConfig.md +1 -1
  85. package/docs/api/interfaces/ButtonProps.md +1 -1
  86. package/docs/api/interfaces/CardProps.md +1 -1
  87. package/docs/api/interfaces/ColorPalette.md +1 -1
  88. package/docs/api/interfaces/ColorShade.md +1 -1
  89. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  90. package/docs/api/interfaces/DataTableAction.md +1 -1
  91. package/docs/api/interfaces/DataTableColumn.md +1 -1
  92. package/docs/api/interfaces/DataTableProps.md +1 -1
  93. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  94. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  95. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  96. package/docs/api/interfaces/EventContextType.md +1 -1
  97. package/docs/api/interfaces/EventLogoProps.md +1 -1
  98. package/docs/api/interfaces/EventProviderProps.md +1 -1
  99. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  100. package/docs/api/interfaces/FileUploadProps.md +1 -1
  101. package/docs/api/interfaces/FooterProps.md +1 -1
  102. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  103. package/docs/api/interfaces/InputProps.md +1 -1
  104. package/docs/api/interfaces/LabelProps.md +1 -1
  105. package/docs/api/interfaces/LoginFormProps.md +1 -1
  106. package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
  107. package/docs/api/interfaces/NavigationContextType.md +1 -1
  108. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  109. package/docs/api/interfaces/NavigationItem.md +1 -1
  110. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  111. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  112. package/docs/api/interfaces/Organisation.md +1 -1
  113. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  114. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  115. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  116. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  117. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  118. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  119. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  120. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  121. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  122. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  123. package/docs/api/interfaces/PaletteData.md +1 -1
  124. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  125. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  126. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  127. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  128. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  129. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  130. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  131. package/docs/api/interfaces/RBACConfig.md +1 -1
  132. package/docs/api/interfaces/RBACContextType.md +1 -1
  133. package/docs/api/interfaces/RBACLogger.md +1 -1
  134. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  135. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  136. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  137. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  138. package/docs/api/interfaces/RouteConfig.md +2 -2
  139. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  140. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  141. package/docs/api/interfaces/StorageConfig.md +1 -1
  142. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  143. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  144. package/docs/api/interfaces/StorageListOptions.md +1 -1
  145. package/docs/api/interfaces/StorageListResult.md +1 -1
  146. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  147. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  148. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  149. package/docs/api/interfaces/StyleImport.md +1 -1
  150. package/docs/api/interfaces/ToastActionElement.md +1 -1
  151. package/docs/api/interfaces/ToastProps.md +1 -1
  152. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  153. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  154. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  155. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  156. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  157. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  158. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  159. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  160. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  161. package/docs/api/interfaces/UserEventAccess.md +1 -1
  162. package/docs/api/interfaces/UserMenuProps.md +1 -1
  163. package/docs/api/interfaces/UserProfile.md +1 -1
  164. package/docs/api/modules.md +204 -200
  165. package/docs/api-reference/components.md +141 -163
  166. package/docs/api-reference/hooks.md +347 -0
  167. package/docs/core-concepts/rbac-system.md +69 -16
  168. package/docs/getting-started/examples/basic-auth-app.md +0 -1
  169. package/docs/implementation-guides/datatable-rbac-usage.md +12 -11
  170. package/docs/implementation-guides/file-upload-storage.md +733 -0
  171. package/docs/implementation-guides/inactivity-tracking.md +779 -0
  172. package/docs/implementation-guides/organisation-security.md +748 -0
  173. package/docs/implementation-guides/public-pages-advanced.md +1022 -0
  174. package/docs/migration/MIGRATION_GUIDE.md +684 -0
  175. package/docs/migration/README.md +13 -2
  176. package/docs/migration/rbac-migration.md +73 -0
  177. package/docs/rbac/examples/rbac-rls-integration-example.md +11 -13
  178. package/docs/style-guide.md +269 -1
  179. package/package.json +1 -1
  180. package/src/__tests__/TESTING_GUIDELINES.md +331 -18
  181. package/src/__tests__/helpers/supabaseMock.ts +99 -0
  182. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -7
  183. package/src/__tests__/shared.ts +6 -0
  184. package/src/components/DataTable/components/ActionButtons.tsx +2 -2
  185. package/src/components/DataTable/components/DataTableCore.tsx +2 -2
  186. package/src/components/DataTable/components/UnifiedTableBody.tsx +1 -1
  187. package/src/components/DataTable/utils/debugTools.ts +2 -2
  188. package/src/components/Dialog/Dialog.test.tsx +12 -2
  189. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +6 -6
  190. package/src/components/ErrorBoundary/ErrorBoundary.tsx +2 -2
  191. package/src/components/FileDisplay.tsx +233 -0
  192. package/src/components/FileUpload.tsx +176 -0
  193. package/src/components/Footer/Footer.test.tsx +7 -7
  194. package/src/components/NavigationMenu/NavigationMenu.test.tsx +13 -6
  195. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +30 -3
  196. package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
  197. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +558 -0
  198. package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
  199. package/src/components/PublicLayout/PublicPageDebugger.tsx +2 -2
  200. package/src/components/PublicLayout/PublicPageDiagnostic.tsx +2 -2
  201. package/src/components/PublicLayout/PublicPageProvider.tsx +2 -2
  202. package/src/components/Select/Select.test.tsx +50 -15
  203. package/src/components/SuperAdminGuard.tsx +2 -2
  204. package/src/components/__tests__/SuperAdminGuard.test.tsx +559 -0
  205. package/src/components/index.ts +0 -183
  206. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
  207. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +1 -1
  208. package/src/hooks/__tests__/useRBAC.unit.test.ts +191 -138
  209. package/src/hooks/public/usePublicEvent.ts +2 -2
  210. package/src/hooks/useAppConfig.ts +3 -3
  211. package/src/hooks/useComponentPerformance.ts +1 -1
  212. package/src/hooks/useDataTablePerformance.ts +1 -1
  213. package/src/hooks/useFileReference.ts +232 -0
  214. package/src/hooks/useOrganisationPermissions.test.ts +254 -344
  215. package/src/hooks/useOrganisationPermissions.ts +15 -7
  216. package/src/hooks/useOrganisationSecurity.test.ts +390 -402
  217. package/src/hooks/usePerformanceMonitor.ts +1 -1
  218. package/src/hooks/usePermissionCache.test.ts +264 -395
  219. package/src/hooks/usePermissionCache.ts +34 -4
  220. package/src/hooks/useSecureDataAccess.test.ts +486 -0
  221. package/src/hooks/useSecureDataAccess.ts +4 -1
  222. package/src/providers/InactivityProvider.tsx +2 -2
  223. package/src/providers/OrganisationProvider.test.simple.tsx +168 -0
  224. package/src/providers/OrganisationProvider.test.tsx +168 -0
  225. package/src/providers/OrganisationProvider.tsx +18 -31
  226. package/src/providers/UnifiedAuthProvider.test.simple.tsx +205 -0
  227. package/src/providers/UnifiedAuthProvider.test.tsx +128 -0
  228. package/src/providers/__tests__/InactivityProvider.test.tsx +3 -4
  229. package/src/providers/__tests__/OrganisationProvider.test.tsx +19 -14
  230. package/src/rbac/__tests__/integration.authflow.test.tsx +123 -0
  231. package/src/rbac/__tests__/integration.navigation.test.tsx +72 -0
  232. package/src/rbac/__tests__/integration.securedata.test.tsx +92 -0
  233. package/src/rbac/__tests__/integration.smoke.test.tsx +73 -0
  234. package/src/rbac/__tests__/rbac-core.test.tsx +26 -22
  235. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +411 -0
  236. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +285 -0
  237. package/src/rbac/__tests__/rbac-functions.test.ts +655 -0
  238. package/src/rbac/__tests__/rbac-integration.test.ts +532 -0
  239. package/src/rbac/__tests__/scenarios.user-role.test.tsx +196 -0
  240. package/src/rbac/api.test.ts +6 -6
  241. package/src/rbac/api.ts +2 -2
  242. package/src/rbac/audit.test.ts +485 -0
  243. package/src/rbac/audit.ts +7 -1
  244. package/src/rbac/cache-invalidation.ts +318 -0
  245. package/src/rbac/cache.test.ts +286 -0
  246. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +559 -0
  247. package/src/rbac/components/EnhancedNavigationMenu.tsx +29 -23
  248. package/src/rbac/components/NavigationProvider.test.tsx +449 -0
  249. package/src/rbac/components/PagePermissionGuard.tsx +4 -4
  250. package/src/rbac/components/PagePermissionProvider.test.tsx +479 -0
  251. package/src/rbac/components/SecureDataProvider.test.tsx +511 -0
  252. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +159 -430
  253. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +4 -5
  254. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +112 -118
  255. package/src/rbac/config.test.ts +410 -0
  256. package/src/rbac/engine.test.simple.ts +237 -0
  257. package/src/rbac/engine.test.ts +233 -0
  258. package/src/rbac/engine.ts +37 -41
  259. package/src/rbac/examples/CompleteRBACExample.tsx +3 -3
  260. package/src/rbac/examples/EventBasedApp.tsx +4 -4
  261. package/src/rbac/hooks/useRBAC.simple.test.ts +16 -0
  262. package/src/rbac/hooks/useRBAC.test.ts +207 -455
  263. package/src/rbac/hooks/useRBAC.ts +30 -22
  264. package/src/rbac/permissions.test.ts +128 -0
  265. package/src/rbac/permissions.ts +56 -141
  266. package/src/rbac/providers/RBACProvider.tsx +1 -1
  267. package/src/rbac/secureClient.test.ts +444 -0
  268. package/src/rbac/security.test.ts +390 -0
  269. package/src/rbac/security.ts +1 -1
  270. package/src/rbac/types.test.ts +382 -0
  271. package/src/rbac/types.ts +2 -2
  272. package/src/styles/core.css +0 -125
  273. package/src/types/file-reference.ts +77 -0
  274. package/src/types/rbac-functions.ts +290 -0
  275. package/src/types/supabase.ts +10 -28
  276. package/src/types/unified.ts +4 -1
  277. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +81 -55
  278. package/src/utils/__tests__/lazyLoad.unit.test.tsx +21 -12
  279. package/src/utils/__tests__/organisationContext.unit.test.ts +13 -7
  280. package/src/utils/__tests__/performanceBudgets.unit.test.ts +3 -3
  281. package/src/utils/__tests__/sessionTracking.unit.test.ts +32 -12
  282. package/src/utils/appConfig.ts +1 -1
  283. package/src/utils/appIdResolver.test.ts +503 -0
  284. package/src/utils/appIdResolver.ts +1 -1
  285. package/src/utils/appNameResolver.test.ts +494 -0
  286. package/src/utils/appNameResolver.ts +3 -2
  287. package/src/utils/bundleAnalysis.ts +3 -3
  288. package/src/utils/debugLogger.ts +1 -1
  289. package/src/utils/file-reference.ts +263 -0
  290. package/src/utils/formatDate.test.ts +2 -2
  291. package/src/utils/organisationContext.test.ts +340 -0
  292. package/src/utils/organisationContext.ts +19 -6
  293. package/src/utils/performanceBudgets.ts +2 -2
  294. package/src/utils/permissionUtils.test.ts +393 -0
  295. package/src/utils/permissionUtils.ts +5 -2
  296. package/src/utils/secureDataAccess.test.ts +715 -0
  297. package/src/utils/secureDataAccess.ts +21 -5
  298. package/src/utils/sessionTracking.ts +34 -4
  299. package/src/utils/storage/__tests__/helpers.unit.test.ts +328 -0
  300. package/src/utils/storage/__tests__/index.unit.test.ts +16 -0
  301. package/src/utils/storage/helpers.ts +20 -25
  302. package/src/utils/storage/index.ts +29 -1
  303. package/src/vite-env.d.ts +17 -0
  304. package/dist/chunk-22KLBHPS.js.map +0 -1
  305. package/dist/chunk-7BNPOCLL.js.map +0 -1
  306. package/dist/chunk-BC3S53OZ.js.map +0 -1
  307. package/dist/chunk-GWSBHC4J.js.map +0 -1
  308. package/dist/chunk-MYP2EGHX.js.map +0 -1
  309. package/dist/chunk-MZBUOP4P.js.map +0 -1
  310. package/dist/chunk-NYUJ4FJR.js.map +0 -1
  311. package/dist/chunk-NZ655MWE.js.map +0 -1
  312. package/dist/chunk-SS3E6QLB.js.map +0 -1
  313. package/dist/chunk-TRIZ7IB7.js.map +0 -1
  314. package/dist/chunk-WJARTBCT.js.map +0 -1
  315. package/dist/chunk-YDJW5XTN.js.map +0 -1
  316. package/docs/print-components/README.md +0 -258
  317. package/docs/print-components/api-reference.md +0 -636
  318. package/docs/print-components/examples/README.md +0 -204
  319. package/docs/print-components/examples/basic-report.tsx +0 -92
  320. package/docs/print-components/examples/card-catalog.tsx +0 -149
  321. package/docs/print-components/examples/cover-page-report.tsx +0 -163
  322. package/docs/print-components/quick-start.md +0 -363
  323. package/src/components/PrintButton/PrintButton.tsx +0 -321
  324. package/src/components/PrintButton/PrintButtonGroup.tsx +0 -84
  325. package/src/components/PrintButton/PrintToolbar.tsx +0 -94
  326. package/src/components/PrintButton/__tests__/PrintButton.test.tsx +0 -271
  327. package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +0 -438
  328. package/src/components/PrintButton/index.ts +0 -33
  329. package/src/components/PrintButton/types.ts +0 -173
  330. package/src/components/PrintCard/PrintCard.tsx +0 -154
  331. package/src/components/PrintCard/PrintCardContent.tsx +0 -57
  332. package/src/components/PrintCard/PrintCardFooter.tsx +0 -60
  333. package/src/components/PrintCard/PrintCardGrid.tsx +0 -91
  334. package/src/components/PrintCard/PrintCardHeader.tsx +0 -78
  335. package/src/components/PrintCard/PrintCardImage.tsx +0 -81
  336. package/src/components/PrintCard/examples/PrintCardShowcase.tsx +0 -239
  337. package/src/components/PrintCard/index.ts +0 -34
  338. package/src/components/PrintCard/types.ts +0 -171
  339. package/src/components/PrintDataTable/PrintDataTable.tsx +0 -215
  340. package/src/components/PrintDataTable/PrintTableGroup.tsx +0 -90
  341. package/src/components/PrintDataTable/PrintTableRow.tsx +0 -76
  342. package/src/components/PrintDataTable/index.ts +0 -25
  343. package/src/components/PrintDataTable/types.ts +0 -67
  344. package/src/components/PrintFooter/PrintFooter.tsx +0 -183
  345. package/src/components/PrintFooter/PrintFooterContent.tsx +0 -71
  346. package/src/components/PrintFooter/PrintFooterInfo.tsx +0 -86
  347. package/src/components/PrintFooter/PrintPageNumber.tsx +0 -90
  348. package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +0 -390
  349. package/src/components/PrintFooter/index.ts +0 -30
  350. package/src/components/PrintFooter/types.ts +0 -149
  351. package/src/components/PrintGrid/PrintGrid.tsx +0 -180
  352. package/src/components/PrintGrid/PrintGridBreakpoint.tsx +0 -109
  353. package/src/components/PrintGrid/PrintGridContainer.tsx +0 -128
  354. package/src/components/PrintGrid/PrintGridItem.tsx +0 -220
  355. package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +0 -359
  356. package/src/components/PrintGrid/index.ts +0 -31
  357. package/src/components/PrintGrid/types.ts +0 -159
  358. package/src/components/PrintHeader/PrintCoverHeader.tsx +0 -230
  359. package/src/components/PrintHeader/PrintHeader.tsx +0 -150
  360. package/src/components/PrintHeader/index.ts +0 -17
  361. package/src/components/PrintHeader/types.ts +0 -42
  362. package/src/components/PrintLayout/PrintLayout.tsx +0 -122
  363. package/src/components/PrintLayout/PrintLayoutContext.tsx +0 -66
  364. package/src/components/PrintLayout/PrintPageBreak.tsx +0 -52
  365. package/src/components/PrintLayout/examples/PrintShowcase.tsx +0 -230
  366. package/src/components/PrintLayout/index.ts +0 -19
  367. package/src/components/PrintLayout/types.ts +0 -37
  368. package/src/components/PrintPageBreak/PrintPageBreak.tsx +0 -120
  369. package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +0 -90
  370. package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +0 -112
  371. package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +0 -279
  372. package/src/components/PrintPageBreak/index.ts +0 -23
  373. package/src/components/PrintPageBreak/types.ts +0 -94
  374. package/src/components/PrintSection/PrintColumn.tsx +0 -104
  375. package/src/components/PrintSection/PrintDivider.tsx +0 -101
  376. package/src/components/PrintSection/PrintSection.tsx +0 -129
  377. package/src/components/PrintSection/PrintSectionContent.tsx +0 -75
  378. package/src/components/PrintSection/PrintSectionHeader.tsx +0 -97
  379. package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +0 -258
  380. package/src/components/PrintSection/index.ts +0 -33
  381. package/src/components/PrintSection/types.ts +0 -155
  382. package/src/components/PrintText/PrintText.tsx +0 -116
  383. package/src/components/PrintText/index.ts +0 -16
  384. package/src/components/PrintText/types.ts +0 -24
  385. package/src/rbac/__tests__/integration.test.tsx +0 -218
  386. package/src/utils/print/PrintDataProcessor.ts +0 -390
  387. package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +0 -397
  388. package/src/utils/print/index.ts +0 -29
  389. package/src/utils/print/types.ts +0 -196
  390. package/src/utils/print/usePrintOptimization.ts +0 -272
  391. /package/dist/{DataTable-7FMFXA7A.js.map → DataTable-4T627QFJ.js.map} +0 -0
  392. /package/dist/{api-H5A3H4IR.js.map → api-LUNF5O6M.js.map} +0 -0
  393. /package/dist/{appNameResolver-7GHF5ED2.js.map → appNameResolver-UURKN7NF.js.map} +0 -0
  394. /package/dist/{audit-BUW3LMJB.js.map → audit-6TOCAMKO.js.map} +0 -0
  395. /package/dist/{chunk-NRK4AIHQ.js.map → chunk-KBRACSJI.js.map} +0 -0
  396. /package/dist/{chunk-6MTY77WU.js.map → chunk-TNMXZLDR.js.map} +0 -0
@@ -92,7 +92,7 @@ interface CacheConfig {
92
92
  const DEFAULT_CONFIG: CacheConfig = {
93
93
  defaultTTL: 5 * 60 * 1000, // 5 minutes
94
94
  maxCacheSize: 1000,
95
- enableLogging: process.env.NODE_ENV === 'development',
95
+ enableLogging: import.meta.env.MODE === 'development',
96
96
  enableAuditTrail: true
97
97
  };
98
98
 
@@ -111,7 +111,7 @@ export function usePermissionCache(config: Partial<CacheConfig> = {}) {
111
111
  cacheMisses: 0,
112
112
  totalChecks: 0,
113
113
  totalResponseTime: 0,
114
- lastInvalidation: Date.now()
114
+ lastInvalidation: 0
115
115
  });
116
116
 
117
117
  // Audit trail
@@ -125,7 +125,8 @@ export function usePermissionCache(config: Partial<CacheConfig> = {}) {
125
125
  }>>([]);
126
126
 
127
127
  // Get base RBAC hook
128
- const { hasPermission: baseHasPermission, user } = useRBAC();
128
+ const rbacHook = useRBAC();
129
+ const { hasPermission: baseHasPermission, user } = rbacHook || {};
129
130
 
130
131
  // Generate cache key
131
132
  const getCacheKey = useCallback((operation: Operation, pageId: string): string => {
@@ -197,6 +198,17 @@ export function usePermissionCache(config: Partial<CacheConfig> = {}) {
197
198
  pageId: string,
198
199
  ttl?: number
199
200
  ): Promise<boolean> => {
201
+ // Validate parameters
202
+ if (!operation || !pageId) {
203
+ console.warn('[PermissionCache] Invalid parameters: operation and pageId are required');
204
+ return false;
205
+ }
206
+
207
+ if (!baseHasPermission) {
208
+ console.warn('[PermissionCache] RBAC not available - permission check failed');
209
+ return false;
210
+ }
211
+
200
212
  const startTime = Date.now();
201
213
  const cacheKey = getCacheKey(operation, pageId);
202
214
  const entry = cache.current.get(cacheKey);
@@ -262,6 +274,24 @@ export function usePermissionCache(config: Partial<CacheConfig> = {}) {
262
274
  permissions: Array<[Operation, string]>,
263
275
  ttl?: number
264
276
  ): Promise<PermissionResult[]> => {
277
+ // Validate parameters
278
+ if (!permissions || !Array.isArray(permissions) || permissions.length === 0) {
279
+ console.warn('[PermissionCache] Invalid permissions array');
280
+ return [];
281
+ }
282
+
283
+ if (!baseHasPermission) {
284
+ console.warn('[PermissionCache] RBAC not available - permission checks failed');
285
+ return permissions.map(([operation, pageId]) => ({
286
+ operation,
287
+ pageId,
288
+ hasPermission: false,
289
+ cached: false,
290
+ responseTime: 0,
291
+ timestamp: Date.now()
292
+ }));
293
+ }
294
+
265
295
  const results: PermissionResult[] = [];
266
296
  const startTime = Date.now();
267
297
 
@@ -433,7 +463,7 @@ export function usePermissionCache(config: Partial<CacheConfig> = {}) {
433
463
  };
434
464
 
435
465
  // Only set up interval in non-test environments to prevent memory leaks during testing
436
- const interval = typeof process !== 'undefined' && process.env.NODE_ENV !== 'test'
466
+ const interval = import.meta.env.MODE !== 'test'
437
467
  ? setInterval(cleanup, 1000)
438
468
  : null;
439
469
 
@@ -0,0 +1,486 @@
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/UnifiedAuthProvider';
14
+ import { useOrganisations } from '../providers/OrganisationProvider';
15
+ import { setOrganisationContext } from '../utils/organisationContext';
16
+ import { createMockSupabaseClient, createMockQueryBuilderWithData } from '../__tests__/helpers/supabaseMock';
17
+
18
+ // Mock the providers
19
+ vi.mock('../providers/UnifiedAuthProvider', () => ({
20
+ useUnifiedAuth: vi.fn()
21
+ }));
22
+
23
+ vi.mock('../providers/OrganisationProvider', () => ({
24
+ useOrganisations: vi.fn()
25
+ }));
26
+
27
+ // Mock the organisation context utility
28
+ vi.mock('../utils/organisationContext', () => ({
29
+ setOrganisationContext: vi.fn()
30
+ }));
31
+
32
+
33
+ describe('useSecureDataAccess', () => {
34
+ const mockUseUnifiedAuth = vi.mocked(useUnifiedAuth);
35
+ const mockUseOrganisations = vi.mocked(useOrganisations);
36
+ const mockSetOrganisationContext = vi.mocked(setOrganisationContext);
37
+
38
+ const mockUser = {
39
+ id: 'user-123',
40
+ email: 'test@example.com'
41
+ };
42
+
43
+ const mockSession = {
44
+ access_token: 'token-123',
45
+ refresh_token: 'refresh-123'
46
+ };
47
+
48
+ // Create proper thenable mock query builder
49
+ const mockQueryBuilder = createMockQueryBuilderWithData([{ id: 'record-123' }]);
50
+ const mockSupabase = createMockSupabaseClient([{ id: 'record-123' }]);
51
+
52
+
53
+ const mockSelectedOrganisation = {
54
+ id: 'org-123',
55
+ name: 'Test Organisation'
56
+ };
57
+
58
+ let freshMockQueryBuilder: any;
59
+ let freshMockSupabase: any;
60
+
61
+ beforeEach(() => {
62
+ vi.clearAllMocks();
63
+
64
+ // Recreate the mock to ensure it's fresh
65
+ freshMockQueryBuilder = createMockQueryBuilderWithData([{ id: 'record-123' }]);
66
+ freshMockSupabase = createMockSupabaseClient([{ id: 'record-123' }]);
67
+ freshMockSupabase.from.mockReturnValue(freshMockQueryBuilder);
68
+
69
+ mockUseUnifiedAuth.mockReturnValue({
70
+ user: mockUser,
71
+ session: mockSession,
72
+ supabase: freshMockSupabase,
73
+ isAuthenticated: true,
74
+ signOut: vi.fn(),
75
+ // Add other required properties
76
+ } as any);
77
+
78
+ mockUseOrganisations.mockReturnValue({
79
+ selectedOrganisation: mockSelectedOrganisation,
80
+ getUserRole: vi.fn().mockReturnValue('member'),
81
+ validateOrganisationAccess: vi.fn().mockResolvedValue(true),
82
+ ensureOrganisationContext: vi.fn().mockReturnValue(mockSelectedOrganisation),
83
+ // Add other required properties
84
+ } as any);
85
+ });
86
+
87
+ afterEach(() => {
88
+ vi.restoreAllMocks();
89
+ });
90
+
91
+ describe('Hook Initialization', () => {
92
+ it('initializes with required dependencies', () => {
93
+ const { result } = renderHook(() => useSecureDataAccess());
94
+
95
+ expect(result.current).toBeDefined();
96
+ expect(result.current.secureQuery).toBeInstanceOf(Function);
97
+ expect(result.current.secureInsert).toBeInstanceOf(Function);
98
+ expect(result.current.secureUpdate).toBeInstanceOf(Function);
99
+ expect(result.current.secureDelete).toBeInstanceOf(Function);
100
+ expect(result.current.secureRpc).toBeInstanceOf(Function);
101
+ expect(result.current.getCurrentOrganisationId()).toBe('org-123');
102
+ });
103
+
104
+ it('depends on useUnifiedAuth hook', () => {
105
+ renderHook(() => useSecureDataAccess());
106
+ expect(mockUseUnifiedAuth).toHaveBeenCalled();
107
+ });
108
+
109
+ it('depends on useOrganisations hook', () => {
110
+ renderHook(() => useSecureDataAccess());
111
+ expect(mockUseOrganisations).toHaveBeenCalled();
112
+ });
113
+ });
114
+
115
+ describe('Secure Query Operations', () => {
116
+ it('executes secure query with organisation filtering', async () => {
117
+ const { result } = renderHook(() => useSecureDataAccess());
118
+
119
+ const data = await result.current.secureQuery('event', '*', { active: true });
120
+
121
+ expect(freshMockSupabase.from).toHaveBeenCalledWith('event');
122
+ expect(freshMockQueryBuilder.select).toHaveBeenCalledWith('*');
123
+ expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
124
+ expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('active', true);
125
+ });
126
+
127
+ it('executes secure query with custom filters', async () => {
128
+ const { result } = renderHook(() => useSecureDataAccess());
129
+
130
+ const data = await result.current.secureQuery('users', '*', { active: true });
131
+
132
+ expect(freshMockSupabase.from).toHaveBeenCalledWith('users');
133
+ expect(freshMockQueryBuilder.select).toHaveBeenCalledWith('*');
134
+ expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('active', true);
135
+ });
136
+
137
+ it('executes secure query with ordering', async () => {
138
+ const { result } = renderHook(() => useSecureDataAccess());
139
+
140
+ const data = await result.current.secureQuery('users', '*', {}, {
141
+ orderBy: 'created_at',
142
+ ascending: false
143
+ });
144
+
145
+ expect(freshMockQueryBuilder.select).toHaveBeenCalledWith('*');
146
+ });
147
+
148
+ it('executes secure query with pagination', async () => {
149
+ const { result } = renderHook(() => useSecureDataAccess());
150
+
151
+ const data = await result.current.secureQuery('users', '*', {}, {
152
+ limit: 10,
153
+ offset: 20
154
+ });
155
+
156
+ expect(freshMockQueryBuilder.select).toHaveBeenCalledWith('*');
157
+ });
158
+
159
+ it('handles query errors gracefully', async () => {
160
+ // Mock error response - the query builder should reject with an error
161
+ freshMockQueryBuilder.then.mockImplementation((resolve, reject) => {
162
+ reject(new Error('Query failed'));
163
+ });
164
+
165
+ const { result } = renderHook(() => useSecureDataAccess());
166
+
167
+ await expect(result.current.secureQuery('users', '*')).rejects.toThrow('Query failed');
168
+ });
169
+ });
170
+
171
+ describe('Secure Insert Operations', () => {
172
+ it('executes secure insert with organisation context', async () => {
173
+ const { result } = renderHook(() => useSecureDataAccess());
174
+
175
+ const data = await result.current.secureInsert('users', { name: 'Test User' });
176
+
177
+ expect(freshMockSupabase.from).toHaveBeenCalledWith('users');
178
+ expect(freshMockQueryBuilder.insert).toHaveBeenCalledWith({
179
+ name: 'Test User',
180
+ organisation_id: 'org-123'
181
+ });
182
+ });
183
+
184
+ it('handles insert errors gracefully', async () => {
185
+ // Mock error response - the entire chain should reject
186
+ freshMockQueryBuilder.single.mockRejectedValue(new Error('Insert failed'));
187
+
188
+ const { result } = renderHook(() => useSecureDataAccess());
189
+
190
+ await expect(result.current.secureInsert('users', { name: 'Test User' })).rejects.toThrow('Insert failed');
191
+ });
192
+
193
+ it('prevents organisation_id override', async () => {
194
+ const { result } = renderHook(() => useSecureDataAccess());
195
+
196
+ const data = await result.current.secureInsert('users', {
197
+ name: 'Test User',
198
+ organisation_id: 'malicious-org-id'
199
+ });
200
+
201
+ expect(freshMockQueryBuilder.insert).toHaveBeenCalledWith({
202
+ name: 'Test User',
203
+ organisation_id: 'org-123' // Should override malicious value
204
+ });
205
+ });
206
+ });
207
+
208
+ describe('Secure Update Operations', () => {
209
+ it('executes secure update with organisation filtering', async () => {
210
+ const { result } = renderHook(() => useSecureDataAccess());
211
+
212
+ const data = await result.current.secureUpdate('event', { event_name: 'Updated Event' }, { id: 'event-123' });
213
+
214
+ expect(freshMockSupabase.from).toHaveBeenCalledWith('event');
215
+ expect(freshMockQueryBuilder.update).toHaveBeenCalledWith({ event_name: 'Updated Event' });
216
+ expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('id', 'event-123');
217
+ expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
218
+ });
219
+
220
+ it('handles update errors gracefully', async () => {
221
+ // Mock error response - mock the entire chain to throw an error
222
+ const errorQueryBuilder = {
223
+ ...freshMockQueryBuilder,
224
+ then: vi.fn().mockImplementation(() => {
225
+ throw new Error('Update failed');
226
+ })
227
+ };
228
+ freshMockSupabase.from.mockReturnValue(errorQueryBuilder);
229
+
230
+ const { result } = renderHook(() => useSecureDataAccess());
231
+
232
+ await expect(result.current.secureUpdate('users', { name: 'Updated User' }, { id: 'user-123' })).rejects.toThrow('Update failed');
233
+ });
234
+
235
+ it('prevents organisation_id updates', async () => {
236
+ const { result } = renderHook(() => useSecureDataAccess());
237
+
238
+ const data = await result.current.secureUpdate('users', {
239
+ name: 'Updated User',
240
+ organisation_id: 'malicious-org-id'
241
+ }, { id: 'user-123' });
242
+
243
+ expect(freshMockQueryBuilder.update).toHaveBeenCalledWith({
244
+ name: 'Updated User'
245
+ // organisation_id should be filtered out
246
+ });
247
+ });
248
+ });
249
+
250
+ describe('Secure Delete Operations', () => {
251
+ it('executes secure delete with organisation filtering', async () => {
252
+ const { result } = renderHook(() => useSecureDataAccess());
253
+
254
+ await result.current.secureDelete('event', { id: 'event-123' });
255
+
256
+ expect(freshMockSupabase.from).toHaveBeenCalledWith('event');
257
+ expect(freshMockQueryBuilder.delete).toHaveBeenCalled();
258
+ expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('id', 'event-123');
259
+ expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
260
+ });
261
+
262
+ it('handles delete errors gracefully', async () => {
263
+ // Mock error response - mock the entire chain to throw an error
264
+ const errorQueryBuilder = {
265
+ ...freshMockQueryBuilder,
266
+ then: vi.fn().mockImplementation(() => {
267
+ throw new Error('Delete failed');
268
+ })
269
+ };
270
+ freshMockSupabase.from.mockReturnValue(errorQueryBuilder);
271
+
272
+ const { result } = renderHook(() => useSecureDataAccess());
273
+
274
+ await expect(result.current.secureDelete('users', { id: 'user-123' })).rejects.toThrow('Delete failed');
275
+ });
276
+ });
277
+
278
+ describe('Secure RPC Operations', () => {
279
+ it('executes secure RPC with organisation context', async () => {
280
+ const { result } = renderHook(() => useSecureDataAccess());
281
+
282
+ const data = await result.current.secureRpc('get_user_data', { user_id: 'user-123' });
283
+
284
+ expect(freshMockSupabase.rpc).toHaveBeenCalledWith('get_user_data', {
285
+ user_id: 'user-123',
286
+ organisation_id: 'org-123'
287
+ });
288
+ });
289
+
290
+ it('handles RPC errors gracefully', async () => {
291
+ // Mock error response - the RPC should reject with an error
292
+ freshMockSupabase.rpc.mockRejectedValue(new Error('RPC failed'));
293
+
294
+ const { result } = renderHook(() => useSecureDataAccess());
295
+
296
+ await expect(result.current.secureRpc('get_user_data')).rejects.toThrow('RPC failed');
297
+ });
298
+ });
299
+
300
+ describe('Organisation Context Management', () => {
301
+ it('sets organisation context before operations', async () => {
302
+ const { result } = renderHook(() => useSecureDataAccess());
303
+
304
+ await result.current.secureQuery('users', '*');
305
+
306
+ expect(mockSetOrganisationContext).toHaveBeenCalledWith(freshMockSupabase, 'org-123');
307
+ });
308
+
309
+ it('handles missing organisation context', () => {
310
+ mockUseOrganisations.mockReturnValue({
311
+ selectedOrganisation: null,
312
+ getUserRole: vi.fn().mockReturnValue('no_access'),
313
+ validateOrganisationAccess: vi.fn().mockResolvedValue(false),
314
+ ensureOrganisationContext: vi.fn().mockImplementation(() => {
315
+ throw new Error('Organisation context is required but not available');
316
+ }),
317
+ // Add other required properties
318
+ } as any);
319
+
320
+ const { result } = renderHook(() => useSecureDataAccess());
321
+
322
+ expect(() => result.current.getCurrentOrganisationId()).toThrow('Organisation context is required for data access');
323
+ });
324
+
325
+ it('validates organisation access before operations', async () => {
326
+ const mockValidateAccess = vi.fn().mockResolvedValue(true);
327
+ mockUseOrganisations.mockReturnValue({
328
+ selectedOrganisation: mockSelectedOrganisation,
329
+ getUserRole: vi.fn().mockReturnValue('member'),
330
+ validateOrganisationAccess: mockValidateAccess,
331
+ ensureOrganisationContext: vi.fn().mockReturnValue(mockSelectedOrganisation),
332
+ // Add other required properties
333
+ } as any);
334
+
335
+ const { result } = renderHook(() => useSecureDataAccess());
336
+
337
+ await result.current.secureQuery('users', '*');
338
+
339
+ // The hook doesn't call validateOrganisationAccess, it calls ensureOrganisationContext
340
+ expect(mockUseOrganisations().ensureOrganisationContext).toHaveBeenCalled();
341
+ });
342
+ });
343
+
344
+ describe('Error Handling', () => {
345
+ it('handles missing user context', () => {
346
+ mockUseUnifiedAuth.mockReturnValue({
347
+ user: null,
348
+ session: null,
349
+ supabase: null,
350
+ isAuthenticated: false,
351
+ signOut: vi.fn(),
352
+ // Add other required properties
353
+ } as any);
354
+
355
+ const { result } = renderHook(() => useSecureDataAccess());
356
+
357
+ expect(() => result.current.getCurrentOrganisationId()).toThrow('No Supabase client available');
358
+ });
359
+
360
+ it('handles missing supabase client', () => {
361
+ mockUseUnifiedAuth.mockReturnValue({
362
+ user: mockUser,
363
+ session: mockSession,
364
+ supabase: null,
365
+ isAuthenticated: true,
366
+ signOut: vi.fn(),
367
+ // Add other required properties
368
+ } as any);
369
+
370
+ const { result } = renderHook(() => useSecureDataAccess());
371
+
372
+ expect(() => result.current.getCurrentOrganisationId()).toThrow('No Supabase client available');
373
+ });
374
+
375
+ it('handles organisation access validation failures', async () => {
376
+ mockUseOrganisations.mockReturnValue({
377
+ selectedOrganisation: mockSelectedOrganisation,
378
+ getUserRole: vi.fn().mockReturnValue('no_access'),
379
+ validateOrganisationAccess: vi.fn().mockResolvedValue(false),
380
+ // Add other required properties
381
+ } as any);
382
+
383
+ const { result } = renderHook(() => useSecureDataAccess());
384
+
385
+ await expect(result.current.secureQuery('users', '*')).rejects.toThrow();
386
+ });
387
+ });
388
+
389
+ describe('Security Features', () => {
390
+ it('prevents data leaks between organisations', async () => {
391
+ const { result } = renderHook(() => useSecureDataAccess());
392
+
393
+ await result.current.secureQuery('event', '*');
394
+
395
+ // Verify organisation_id filter is always applied
396
+ expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
397
+ });
398
+
399
+ it('prevents organisation_id manipulation in inserts', async () => {
400
+ const { result } = renderHook(() => useSecureDataAccess());
401
+
402
+ await result.current.secureInsert('users', {
403
+ name: 'Test User',
404
+ organisation_id: 'malicious-org-id'
405
+ });
406
+
407
+ expect(freshMockQueryBuilder.insert).toHaveBeenCalledWith({
408
+ name: 'Test User',
409
+ organisation_id: 'org-123' // Should override malicious value
410
+ });
411
+ });
412
+
413
+ it('prevents organisation_id manipulation in updates', async () => {
414
+ const { result } = renderHook(() => useSecureDataAccess());
415
+
416
+ await result.current.secureUpdate('users', {
417
+ name: 'Updated User',
418
+ organisation_id: 'malicious-org-id'
419
+ }, { id: 'user-123' });
420
+
421
+ expect(freshMockQueryBuilder.update).toHaveBeenCalledWith({
422
+ name: 'Updated User'
423
+ // organisation_id should be filtered out
424
+ });
425
+ });
426
+ });
427
+
428
+ describe('Performance', () => {
429
+ it('memoizes results to prevent unnecessary re-renders', () => {
430
+ const { result, rerender } = renderHook(() => useSecureDataAccess());
431
+ const initialSecureQuery = result.current.secureQuery;
432
+ const initialGetCurrentOrganisationId = result.current.getCurrentOrganisationId;
433
+
434
+ // Re-render with same props
435
+ rerender();
436
+
437
+ // Should not cause additional renders due to memoization
438
+ expect(result.current.secureQuery).toBe(initialSecureQuery);
439
+ expect(result.current.getCurrentOrganisationId).toBe(initialGetCurrentOrganisationId);
440
+ });
441
+
442
+ it('handles organisation changes efficiently', () => {
443
+ const { result, rerender } = renderHook(() => useSecureDataAccess());
444
+
445
+ expect(result.current.getCurrentOrganisationId()).toBe('org-123');
446
+
447
+ // Change organisation
448
+ const newOrganisation = { id: 'org-456', name: 'New Organisation' };
449
+ mockUseOrganisations.mockReturnValue({
450
+ selectedOrganisation: newOrganisation,
451
+ getUserRole: vi.fn().mockReturnValue('member'),
452
+ validateOrganisationAccess: vi.fn().mockResolvedValue(true),
453
+ ensureOrganisationContext: vi.fn().mockReturnValue(newOrganisation),
454
+ // Add other required properties
455
+ } as any);
456
+
457
+ rerender();
458
+
459
+ expect(result.current.getCurrentOrganisationId()).toBe('org-456');
460
+ });
461
+ });
462
+
463
+ describe('Integration with Providers', () => {
464
+ it('integrates with useUnifiedAuth hook correctly', () => {
465
+ renderHook(() => useSecureDataAccess());
466
+ expect(mockUseUnifiedAuth).toHaveBeenCalled();
467
+ });
468
+
469
+ it('integrates with useOrganisations hook correctly', () => {
470
+ renderHook(() => useSecureDataAccess());
471
+ expect(mockUseOrganisations).toHaveBeenCalled();
472
+ });
473
+
474
+ it('uses organisation context from provider', () => {
475
+ const { result } = renderHook(() => useSecureDataAccess());
476
+
477
+ expect(result.current.getCurrentOrganisationId()).toBe('org-123');
478
+ });
479
+
480
+ it('validates organisation access through provider', () => {
481
+ // This test is removed as validateOrganisationAccess is not used in useSecureDataAccess
482
+ // The hook only calls ensureOrganisationContext during operations
483
+ expect(true).toBe(true);
484
+ });
485
+ });
486
+ });
@@ -335,10 +335,13 @@ export function useSecureDataAccess(): SecureDataAccessReturn {
335
335
  // Set organisation context in database session
336
336
  await setOrganisationContextInSession(organisationId);
337
337
 
338
+ // Filter out organisation_id from data to prevent manipulation
339
+ const { organisation_id, ...secureData } = data;
340
+
338
341
  // Build update query with organisation filter
339
342
  let query = supabase!
340
343
  .from(table)
341
- .update(data);
344
+ .update(secureData);
342
345
 
343
346
  // Add organisation filter only if table has organisation_id column
344
347
  const tablesWithOrganisation = [
@@ -75,7 +75,7 @@ export function InactivityProvider({
75
75
  // Production safety check for inactivity feature
76
76
  useEffect(() => {
77
77
  if (typeof window !== 'undefined') {
78
- const isProduction = process.env.NODE_ENV === 'production';
78
+ const isProduction = import.meta.env.MODE === 'production';
79
79
 
80
80
  if (isProduction && dangerouslyDisableInactivity) {
81
81
  console.error('[InactivityProvider] CRITICAL: dangerouslyDisableInactivity is not allowed in production! Auto-enabling inactivity feature.');
@@ -89,7 +89,7 @@ export function InactivityProvider({
89
89
 
90
90
  // Inactivity tracking
91
91
  const isInactivityEnabled = typeof window !== 'undefined' &&
92
- (process.env.NODE_ENV !== 'production' ? !dangerouslyDisableInactivity : true);
92
+ (import.meta.env.MODE !== 'production' ? !dangerouslyDisableInactivity : true);
93
93
 
94
94
  const {
95
95
  isIdle,