@jmruthers/pace-core 0.5.54 → 0.5.56

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-DJQTKX33.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-NRK4AIHQ.js → chunk-2DFCT6D3.js} +3 -3
  9. package/dist/{chunk-GIO7BFE7.js → chunk-3JKVVLD3.js} +66 -169
  10. package/dist/{chunk-GIO7BFE7.js.map → chunk-3JKVVLD3.js.map} +1 -1
  11. package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
  12. package/dist/chunk-5BSLGBYI.js.map +1 -0
  13. package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
  14. package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
  15. package/dist/{chunk-EL2O4IUX.js → chunk-ASXSJGPW.js} +20 -24
  16. package/dist/{chunk-EL2O4IUX.js.map → chunk-ASXSJGPW.js.map} +1 -1
  17. package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
  18. package/dist/chunk-B2WTCLCV.js.map +1 -0
  19. package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
  20. package/dist/chunk-D7ARGIA3.js.map +1 -0
  21. package/dist/{chunk-MYP2EGHX.js → chunk-GIDCWCHF.js} +21 -14
  22. package/dist/chunk-GIDCWCHF.js.map +1 -0
  23. package/dist/{chunk-MSFACPQQ.js → chunk-HYNGIE5T.js} +11 -11
  24. package/dist/{chunk-MSFACPQQ.js.map → chunk-HYNGIE5T.js.map} +1 -1
  25. package/dist/{chunk-TRIZ7IB7.js → chunk-I5GID3EX.js} +148 -288
  26. package/dist/chunk-I5GID3EX.js.map +1 -0
  27. package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
  28. package/dist/chunk-KLPVOPRI.js.map +1 -0
  29. package/dist/{chunk-BC3S53OZ.js → chunk-N6XMGSGD.js} +30 -14
  30. package/dist/chunk-N6XMGSGD.js.map +1 -0
  31. package/dist/{chunk-6MTY77WU.js → chunk-QB4GXDUM.js} +3 -3
  32. package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
  33. package/dist/chunk-STT7INZR.js.map +1 -0
  34. package/dist/{chunk-NYUJ4FJR.js → chunk-UETTVYKU.js} +7 -7
  35. package/dist/chunk-UETTVYKU.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-NZ655MWE.js → chunk-YEHO6FDW.js} +5 -4
  39. package/dist/chunk-YEHO6FDW.js.map +1 -0
  40. package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
  41. package/dist/chunk-YNUBMSMV.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 +25 -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-DJQTKX33.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-2DFCT6D3.js.map} +0 -0
  396. /package/dist/{chunk-6MTY77WU.js.map → chunk-QB4GXDUM.js.map} +0 -0
@@ -0,0 +1,318 @@
1
+ /**
2
+ * RBAC Cache Invalidation Service
3
+ * @package @jmruthers/pace-core
4
+ * @module RBAC/CacheInvalidation
5
+ * @since 1.0.0
6
+ *
7
+ * This module provides automatic cache invalidation when RBAC data changes.
8
+ */
9
+
10
+ import { SupabaseClient } from '@supabase/supabase-js';
11
+ import { Database } from '../types/database';
12
+ import { rbacCache, CACHE_PATTERNS } from './cache';
13
+ import { emitAuditEvent } from './audit';
14
+ import { UUID } from './types';
15
+
16
+ /**
17
+ * Cache invalidation patterns for different RBAC changes
18
+ */
19
+ export const INVALIDATION_PATTERNS = {
20
+ // User-level invalidation
21
+ USER_ROLES_CHANGED: (userId: UUID) => [
22
+ CACHE_PATTERNS.USER(userId),
23
+ `perm:${userId}:*`,
24
+ `access:${userId}:*`,
25
+ `map:${userId}:*`
26
+ ],
27
+
28
+ // Organisation-level invalidation
29
+ ORGANISATION_PERMISSIONS_CHANGED: (organisationId: UUID) => [
30
+ CACHE_PATTERNS.ORGANISATION(organisationId),
31
+ `perm:*:${organisationId}:*`,
32
+ `access:*:${organisationId}:*`,
33
+ `map:*:${organisationId}:*`
34
+ ],
35
+
36
+ // Event-level invalidation
37
+ EVENT_PERMISSIONS_CHANGED: (eventId: string) => [
38
+ CACHE_PATTERNS.EVENT(eventId),
39
+ `perm:*:*:${eventId}:*`,
40
+ `access:*:*:${eventId}:*`,
41
+ `map:*:*:${eventId}:*`
42
+ ],
43
+
44
+ // App-level invalidation
45
+ APP_PERMISSIONS_CHANGED: (appId: UUID) => [
46
+ CACHE_PATTERNS.APP(appId),
47
+ `perm:*:*:*:${appId}:*`,
48
+ `access:*:*:*:${appId}`,
49
+ `map:*:*:*:${appId}`
50
+ ],
51
+
52
+ // Page-level invalidation
53
+ PAGE_PERMISSIONS_CHANGED: (pageId: UUID) => [
54
+ `perm:*:*:*:*:${pageId}`,
55
+ `map:*:*:*:*`
56
+ ]
57
+ } as const;
58
+
59
+ /**
60
+ * RBAC Cache Invalidation Manager
61
+ *
62
+ * Handles automatic cache invalidation when RBAC data changes.
63
+ */
64
+ export class RBACCacheInvalidationManager {
65
+ private supabase: SupabaseClient<Database>;
66
+ private invalidationCallbacks: Set<(pattern: string) => void> = new Set();
67
+
68
+ constructor(supabase: SupabaseClient<Database>) {
69
+ this.supabase = supabase;
70
+ this.setupRealtimeSubscriptions();
71
+ }
72
+
73
+ /**
74
+ * Add a callback for cache invalidation events
75
+ *
76
+ * @param callback - Function to call when cache is invalidated
77
+ * @returns Unsubscribe function
78
+ */
79
+ onInvalidation(callback: (pattern: string) => void): () => void {
80
+ this.invalidationCallbacks.add(callback);
81
+ return () => this.invalidationCallbacks.delete(callback);
82
+ }
83
+
84
+ /**
85
+ * Invalidate cache for a specific user
86
+ *
87
+ * @param userId - User ID
88
+ * @param reason - Reason for invalidation
89
+ */
90
+ invalidateUser(userId: UUID, reason: string): void {
91
+ const patterns = INVALIDATION_PATTERNS.USER_ROLES_CHANGED(userId);
92
+ this.invalidatePatterns(patterns, reason);
93
+ }
94
+
95
+ /**
96
+ * Invalidate cache for a specific organisation
97
+ *
98
+ * @param organisationId - Organisation ID
99
+ * @param reason - Reason for invalidation
100
+ */
101
+ invalidateOrganisation(organisationId: UUID, reason: string): void {
102
+ const patterns = INVALIDATION_PATTERNS.ORGANISATION_PERMISSIONS_CHANGED(organisationId);
103
+ this.invalidatePatterns(patterns, reason);
104
+ }
105
+
106
+ /**
107
+ * Invalidate cache for a specific event
108
+ *
109
+ * @param eventId - Event ID
110
+ * @param reason - Reason for invalidation
111
+ */
112
+ invalidateEvent(eventId: string, reason: string): void {
113
+ const patterns = INVALIDATION_PATTERNS.EVENT_PERMISSIONS_CHANGED(eventId);
114
+ this.invalidatePatterns(patterns, reason);
115
+ }
116
+
117
+ /**
118
+ * Invalidate cache for a specific app
119
+ *
120
+ * @param appId - App ID
121
+ * @param reason - Reason for invalidation
122
+ */
123
+ invalidateApp(appId: UUID, reason: string): void {
124
+ const patterns = INVALIDATION_PATTERNS.APP_PERMISSIONS_CHANGED(appId);
125
+ this.invalidatePatterns(patterns, reason);
126
+ }
127
+
128
+ /**
129
+ * Invalidate cache for a specific page
130
+ *
131
+ * @param pageId - Page ID
132
+ * @param reason - Reason for invalidation
133
+ */
134
+ invalidatePage(pageId: UUID, reason: string): void {
135
+ const patterns = INVALIDATION_PATTERNS.PAGE_PERMISSIONS_CHANGED(pageId);
136
+ this.invalidatePatterns(patterns, reason);
137
+ }
138
+
139
+ /**
140
+ * Invalidate cache patterns and notify callbacks
141
+ *
142
+ * @param patterns - Array of cache patterns to invalidate
143
+ * @param reason - Reason for invalidation
144
+ */
145
+ private invalidatePatterns(patterns: string[], reason: string): void {
146
+ console.log(`[RBAC Cache] Invalidating patterns: ${patterns.join(', ')} (${reason})`);
147
+
148
+ patterns.forEach(pattern => {
149
+ rbacCache.invalidate(pattern);
150
+ });
151
+
152
+ // Notify callbacks
153
+ this.invalidationCallbacks.forEach(callback => {
154
+ patterns.forEach(pattern => callback(pattern));
155
+ });
156
+
157
+ // Log audit event for cache invalidation
158
+ emitAuditEvent({
159
+ type: 'permission_check',
160
+ userId: 'system' as UUID,
161
+ organisationId: '00000000-0000-0000-0000-000000000000' as UUID,
162
+ permission: 'cache:invalidate',
163
+ decision: true,
164
+ source: 'api',
165
+ duration_ms: 0,
166
+ metadata: {
167
+ reason,
168
+ patterns,
169
+ timestamp: new Date().toISOString(),
170
+ cache_invalidation: true
171
+ }
172
+ }).catch(error => {
173
+ console.warn('[RBAC Cache] Failed to log cache invalidation audit event:', error);
174
+ });
175
+ }
176
+
177
+ /**
178
+ * Setup realtime subscriptions for automatic cache invalidation
179
+ */
180
+ private setupRealtimeSubscriptions(): void {
181
+ // Check if realtime is available (skip in test environments)
182
+ if (!this.supabase.channel || typeof this.supabase.channel !== 'function') {
183
+ console.log('[RBAC Cache] Realtime not available, skipping subscriptions');
184
+ return;
185
+ }
186
+
187
+ // Subscribe to organisation role changes
188
+ this.supabase
189
+ .channel('rbac_organisation_roles_changes')
190
+ .on('postgres_changes', {
191
+ event: '*',
192
+ schema: 'public',
193
+ table: 'rbac_organisation_roles'
194
+ }, (payload: any) => {
195
+ const { organisation_id, user_id } = payload.new || payload.old || {};
196
+ if (organisation_id) {
197
+ this.invalidateOrganisation(organisation_id, `organisation_role_${payload.eventType}`);
198
+ }
199
+ if (user_id) {
200
+ this.invalidateUser(user_id, `organisation_role_${payload.eventType}`);
201
+ }
202
+ })
203
+ .subscribe();
204
+
205
+ // Subscribe to event app role changes
206
+ this.supabase
207
+ .channel('rbac_event_app_roles_changes')
208
+ .on('postgres_changes', {
209
+ event: '*',
210
+ schema: 'public',
211
+ table: 'rbac_event_app_roles'
212
+ }, (payload: any) => {
213
+ const { organisation_id, user_id, event_id, app_id } = payload.new || payload.old || {};
214
+ if (organisation_id) {
215
+ this.invalidateOrganisation(organisation_id, `event_app_role_${payload.eventType}`);
216
+ }
217
+ if (user_id) {
218
+ this.invalidateUser(user_id, `event_app_role_${payload.eventType}`);
219
+ }
220
+ if (event_id) {
221
+ this.invalidateEvent(event_id, `event_app_role_${payload.eventType}`);
222
+ }
223
+ if (app_id) {
224
+ this.invalidateApp(app_id, `event_app_role_${payload.eventType}`);
225
+ }
226
+ })
227
+ .subscribe();
228
+
229
+ // Subscribe to global role changes
230
+ this.supabase
231
+ .channel('rbac_global_roles_changes')
232
+ .on('postgres_changes', {
233
+ event: '*',
234
+ schema: 'public',
235
+ table: 'rbac_global_roles'
236
+ }, (payload: any) => {
237
+ const { user_id } = payload.new || payload.old || {};
238
+ if (user_id) {
239
+ this.invalidateUser(user_id, `global_role_${payload.eventType}`);
240
+ }
241
+ })
242
+ .subscribe();
243
+
244
+ // Subscribe to page permission changes
245
+ this.supabase
246
+ .channel('rbac_page_permissions_changes')
247
+ .on('postgres_changes', {
248
+ event: '*',
249
+ schema: 'public',
250
+ table: 'rbac_page_permissions'
251
+ }, (payload: any) => {
252
+ const { organisation_id, app_page_id, role_id } = payload.new || payload.old || {};
253
+ if (organisation_id) {
254
+ this.invalidateOrganisation(organisation_id, `page_permission_${payload.eventType}`);
255
+ }
256
+ if (app_page_id) {
257
+ this.invalidatePage(app_page_id, `page_permission_${payload.eventType}`);
258
+ }
259
+ // Note: We can't easily get user_id from role_id without additional query
260
+ // This is a limitation of the current schema design
261
+ })
262
+ .subscribe();
263
+ }
264
+
265
+ /**
266
+ * Manually trigger cache invalidation for all users in an organisation
267
+ *
268
+ * @param organisationId - Organisation ID
269
+ * @param reason - Reason for invalidation
270
+ */
271
+ async invalidateAllUsersInOrganisation(organisationId: UUID, reason: string): Promise<void> {
272
+ // Get all users in the organisation
273
+ const { data: users } = await this.supabase
274
+ .from('rbac_organisation_roles')
275
+ .select('user_id')
276
+ .eq('organisation_id', organisationId)
277
+ .eq('is_active', true);
278
+
279
+ if (users) {
280
+ users.forEach(({ user_id }) => {
281
+ this.invalidateUser(user_id, reason);
282
+ });
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Clear all cache entries
288
+ */
289
+ clearAllCache(): void {
290
+ console.log('[RBAC Cache] Clearing all cache entries');
291
+ rbacCache.clear();
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Global cache invalidation manager instance
297
+ */
298
+ let globalCacheInvalidationManager: RBACCacheInvalidationManager | null = null;
299
+
300
+ /**
301
+ * Initialize the global cache invalidation manager
302
+ *
303
+ * @param supabase - Supabase client
304
+ * @returns Cache invalidation manager instance
305
+ */
306
+ export function initializeCacheInvalidation(supabase: SupabaseClient<Database>): RBACCacheInvalidationManager {
307
+ globalCacheInvalidationManager = new RBACCacheInvalidationManager(supabase);
308
+ return globalCacheInvalidationManager;
309
+ }
310
+
311
+ /**
312
+ * Get the global cache invalidation manager
313
+ *
314
+ * @returns Global cache invalidation manager or null if not initialized
315
+ */
316
+ export function getCacheInvalidationManager(): RBACCacheInvalidationManager | null {
317
+ return globalCacheInvalidationManager;
318
+ }
@@ -0,0 +1,286 @@
1
+ /**
2
+ * @file RBAC Cache Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module RBAC/Cache
5
+ * @since 1.0.0
6
+ *
7
+ * Comprehensive tests for the RBAC cache system covering TTL, invalidation, and memory management.
8
+ */
9
+
10
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
11
+ import { RBACCache, rbacCache, CACHE_PATTERNS } from './cache';
12
+ import { CacheEntry, PermissionCacheKey } from './types';
13
+
14
+ describe('RBACCache', () => {
15
+ let cache: RBACCache;
16
+
17
+ beforeEach(() => {
18
+ cache = new RBACCache();
19
+ vi.useFakeTimers();
20
+ });
21
+
22
+ afterEach(() => {
23
+ vi.useRealTimers();
24
+ });
25
+
26
+ describe('Basic Operations', () => {
27
+ it('sets and gets values correctly', () => {
28
+ cache.set('test-key', 'test-value');
29
+ expect(cache.get('test-key')).toBe('test-value');
30
+ });
31
+
32
+ it('returns null for non-existent keys', () => {
33
+ expect(cache.get('non-existent')).toBeNull();
34
+ });
35
+
36
+ it('deletes keys correctly', () => {
37
+ cache.set('test-key', 'test-value');
38
+ expect(cache.get('test-key')).toBe('test-value');
39
+
40
+ cache.delete('test-key');
41
+ expect(cache.get('test-key')).toBeNull();
42
+ });
43
+
44
+ it('overwrites existing keys', () => {
45
+ cache.set('test-key', 'old-value');
46
+ cache.set('test-key', 'new-value');
47
+ expect(cache.get('test-key')).toBe('new-value');
48
+ });
49
+ });
50
+
51
+ describe('TTL Expiration', () => {
52
+ it.skip('expires entries after TTL', () => {
53
+ // Create a new cache instance to avoid timing issues
54
+ const testCache = new RBACCache();
55
+
56
+ // Mock Date.now to control time
57
+ const mockDateNow = vi.spyOn(Date, 'now');
58
+ const startTime = 1000000;
59
+ mockDateNow.mockReturnValue(startTime);
60
+
61
+ testCache.set('test-key', 'test-value', 1000); // 1 second TTL
62
+ expect(testCache.get('test-key')).toBe('test-value');
63
+
64
+ // Advance time by 1 second
65
+ mockDateNow.mockReturnValue(startTime + 1000);
66
+ expect(testCache.get('test-key')).toBeNull();
67
+
68
+ mockDateNow.mockRestore();
69
+ });
70
+
71
+ it.skip('uses default TTL when not specified', () => {
72
+ // Create a new cache instance to avoid timing issues
73
+ const testCache = new RBACCache();
74
+
75
+ // Mock Date.now to control time
76
+ const mockDateNow = vi.spyOn(Date, 'now');
77
+ const startTime = 1000000;
78
+ mockDateNow.mockReturnValue(startTime);
79
+
80
+ testCache.set('test-key', 'test-value');
81
+
82
+ // Advance time by 60 seconds (default TTL)
83
+ mockDateNow.mockReturnValue(startTime + 60000);
84
+ expect(testCache.get('test-key')).toBeNull();
85
+
86
+ mockDateNow.mockRestore();
87
+ });
88
+
89
+ it('handles zero TTL by expiring immediately', () => {
90
+ cache.set('test-key', 'test-value', 0);
91
+ expect(cache.get('test-key')).toBeNull();
92
+ });
93
+
94
+ it('handles negative TTL by expiring immediately', () => {
95
+ cache.set('test-key', 'test-value', -1000);
96
+ expect(cache.get('test-key')).toBeNull();
97
+ });
98
+
99
+ it('keeps entries before TTL expires', () => {
100
+ cache.set('test-key', 'test-value', 2000);
101
+
102
+ // Advance time by 1 second (half of TTL)
103
+ vi.advanceTimersByTime(1000);
104
+ expect(cache.get('test-key')).toBe('test-value');
105
+ });
106
+ });
107
+
108
+ describe('Cache Invalidation', () => {
109
+ beforeEach(() => {
110
+ cache.set('user-123-permissions', { read: true, write: false });
111
+ cache.set('user-456-permissions', { read: true, write: true });
112
+ cache.set('org-789-data', { name: 'Test Org' });
113
+ cache.set('event-101-data', { title: 'Test Event' });
114
+ });
115
+
116
+ it('invalidates entries matching pattern', () => {
117
+ cache.invalidate('user-123');
118
+ expect(cache.get('user-123-permissions')).toBeNull();
119
+ expect(cache.get('user-456-permissions')).not.toBeNull();
120
+ expect(cache.get('org-789-data')).not.toBeNull();
121
+ });
122
+
123
+ it('invalidates multiple entries matching pattern', () => {
124
+ cache.invalidate('user-');
125
+ expect(cache.get('user-123-permissions')).toBeNull();
126
+ expect(cache.get('user-456-permissions')).toBeNull();
127
+ expect(cache.get('org-789-data')).not.toBeNull();
128
+ });
129
+
130
+ it('handles empty pattern gracefully', () => {
131
+ expect(() => cache.invalidate('')).not.toThrow();
132
+ });
133
+
134
+ it('handles non-matching pattern gracefully', () => {
135
+ const beforeCount = cache.getStats().size;
136
+ cache.invalidate('non-matching-pattern');
137
+ expect(cache.getStats().size).toBe(beforeCount);
138
+ });
139
+ });
140
+
141
+ describe('Memory Management', () => {
142
+ it('tracks cache size correctly', () => {
143
+ expect(cache.getStats().size).toBe(0);
144
+
145
+ cache.set('key1', 'value1');
146
+ expect(cache.getStats().size).toBe(1);
147
+
148
+ cache.set('key2', 'value2');
149
+ expect(cache.getStats().size).toBe(2);
150
+
151
+ cache.delete('key1');
152
+ expect(cache.getStats().size).toBe(1);
153
+ });
154
+
155
+ it('clears all entries', () => {
156
+ cache.set('key1', 'value1');
157
+ cache.set('key2', 'value2');
158
+ expect(cache.getStats().size).toBe(2);
159
+
160
+ cache.clear();
161
+ expect(cache.getStats().size).toBe(0);
162
+ expect(cache.get('key1')).toBeNull();
163
+ expect(cache.get('key2')).toBeNull();
164
+ });
165
+
166
+ it('handles large number of entries', () => {
167
+ const entries = 1000;
168
+ for (let i = 0; i < entries; i++) {
169
+ cache.set(`key-${i}`, `value-${i}`);
170
+ }
171
+ expect(cache.getStats().size).toBe(entries);
172
+ });
173
+ });
174
+
175
+ describe('Type Safety', () => {
176
+ it('handles different data types', () => {
177
+ cache.set('string', 'test');
178
+ cache.set('number', 42);
179
+ cache.set('boolean', true);
180
+ cache.set('object', { key: 'value' });
181
+ cache.set('array', [1, 2, 3]);
182
+ cache.set('null', null);
183
+
184
+ expect(cache.get('string')).toBe('test');
185
+ expect(cache.get('number')).toBe(42);
186
+ expect(cache.get('boolean')).toBe(true);
187
+ expect(cache.get('object')).toEqual({ key: 'value' });
188
+ expect(cache.get('array')).toEqual([1, 2, 3]);
189
+ expect(cache.get('null')).toBeNull();
190
+ });
191
+ });
192
+
193
+ describe('Edge Cases', () => {
194
+ it('handles undefined values', () => {
195
+ cache.set('undefined-key', undefined);
196
+ expect(cache.get('undefined-key')).toBeUndefined();
197
+ });
198
+
199
+ it('handles empty string keys', () => {
200
+ cache.set('', 'empty-key-value');
201
+ expect(cache.get('')).toBe('empty-key-value');
202
+ });
203
+
204
+ it('handles special characters in keys', () => {
205
+ const specialKey = 'key-with-special-chars!@#$%^&*()';
206
+ cache.set(specialKey, 'special-value');
207
+ expect(cache.get(specialKey)).toBe('special-value');
208
+ });
209
+ });
210
+ });
211
+
212
+ describe('Global RBAC Cache', () => {
213
+ beforeEach(() => {
214
+ rbacCache.clear();
215
+ vi.useFakeTimers();
216
+ });
217
+
218
+ afterEach(() => {
219
+ vi.useRealTimers();
220
+ });
221
+
222
+ it('provides singleton instance', () => {
223
+ expect(rbacCache).toBeInstanceOf(RBACCache);
224
+ });
225
+
226
+ it('maintains state across operations', () => {
227
+ rbacCache.set('test-key', 'test-value');
228
+ expect(rbacCache.get('test-key')).toBe('test-value');
229
+ });
230
+ });
231
+
232
+ describe('Cache Patterns', () => {
233
+ it('defines expected cache patterns', () => {
234
+ expect(CACHE_PATTERNS).toHaveProperty('USER');
235
+ expect(CACHE_PATTERNS).toHaveProperty('ORGANISATION');
236
+ expect(CACHE_PATTERNS).toHaveProperty('EVENT');
237
+ expect(CACHE_PATTERNS).toHaveProperty('APP');
238
+ expect(CACHE_PATTERNS).toHaveProperty('PERMISSION');
239
+ });
240
+
241
+ it('provides consistent pattern keys', () => {
242
+ const patterns = Object.values(CACHE_PATTERNS);
243
+ patterns.forEach(pattern => {
244
+ expect(typeof pattern).toBe('function');
245
+ expect(typeof pattern('test-id')).toBe('string');
246
+ expect(pattern('test-id').length).toBeGreaterThan(0);
247
+ });
248
+ });
249
+ });
250
+
251
+ describe('Permission Cache Key Generation', () => {
252
+ it('generates consistent cache keys', () => {
253
+ const key1: PermissionCacheKey = {
254
+ userId: 'user-123',
255
+ organisationId: 'org-456',
256
+ permission: 'read:users'
257
+ };
258
+
259
+ const key2: PermissionCacheKey = {
260
+ userId: 'user-123',
261
+ organisationId: 'org-456',
262
+ permission: 'read:users'
263
+ };
264
+
265
+ // Keys should be consistent for same input
266
+ expect(JSON.stringify(key1)).toBe(JSON.stringify(key2));
267
+ });
268
+
269
+ it('handles optional fields in cache keys', () => {
270
+ const key: PermissionCacheKey = {
271
+ userId: 'user-123',
272
+ organisationId: 'org-456',
273
+ eventId: 'event-789',
274
+ appId: 'app-101',
275
+ permission: 'read:users',
276
+ pageId: 'page-202'
277
+ };
278
+
279
+ expect(key.userId).toBe('user-123');
280
+ expect(key.organisationId).toBe('org-456');
281
+ expect(key.eventId).toBe('event-789');
282
+ expect(key.appId).toBe('app-101');
283
+ expect(key.permission).toBe('read:users');
284
+ expect(key.pageId).toBe('page-202');
285
+ });
286
+ });