@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.
- package/README.md +0 -4
- package/dist/{DataTable-7FMFXA7A.js → DataTable-DJQTKX33.js} +11 -11
- package/dist/{PublicLoadingSpinner-Bq_-BeK-.d.ts → PublicLoadingSpinner-SL8WaQN7.d.ts} +2 -21
- package/dist/{api-H5A3H4IR.js → api-LUNF5O6M.js} +3 -3
- package/dist/{appConfig-BVGyuvI7.d.ts → appConfig-DjpeG6P-.d.ts} +9 -1
- package/dist/{appNameResolver-7GHF5ED2.js → appNameResolver-UURKN7NF.js} +2 -2
- package/dist/{audit-BUW3LMJB.js → audit-6TOCAMKO.js} +2 -2
- package/dist/{chunk-NRK4AIHQ.js → chunk-2DFCT6D3.js} +3 -3
- package/dist/{chunk-GIO7BFE7.js → chunk-3JKVVLD3.js} +66 -169
- package/dist/{chunk-GIO7BFE7.js.map → chunk-3JKVVLD3.js.map} +1 -1
- package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
- package/dist/chunk-5BSLGBYI.js.map +1 -0
- package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
- package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
- package/dist/{chunk-EL2O4IUX.js → chunk-ASXSJGPW.js} +20 -24
- package/dist/{chunk-EL2O4IUX.js.map → chunk-ASXSJGPW.js.map} +1 -1
- package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
- package/dist/chunk-B2WTCLCV.js.map +1 -0
- package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
- package/dist/chunk-D7ARGIA3.js.map +1 -0
- package/dist/{chunk-MYP2EGHX.js → chunk-GIDCWCHF.js} +21 -14
- package/dist/chunk-GIDCWCHF.js.map +1 -0
- package/dist/{chunk-MSFACPQQ.js → chunk-HYNGIE5T.js} +11 -11
- package/dist/{chunk-MSFACPQQ.js.map → chunk-HYNGIE5T.js.map} +1 -1
- package/dist/{chunk-TRIZ7IB7.js → chunk-I5GID3EX.js} +148 -288
- package/dist/chunk-I5GID3EX.js.map +1 -0
- package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
- package/dist/chunk-KLPVOPRI.js.map +1 -0
- package/dist/{chunk-BC3S53OZ.js → chunk-N6XMGSGD.js} +30 -14
- package/dist/chunk-N6XMGSGD.js.map +1 -0
- package/dist/{chunk-6MTY77WU.js → chunk-QB4GXDUM.js} +3 -3
- package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
- package/dist/chunk-STT7INZR.js.map +1 -0
- package/dist/{chunk-NYUJ4FJR.js → chunk-UETTVYKU.js} +7 -7
- package/dist/chunk-UETTVYKU.js.map +1 -0
- package/dist/{chunk-22KLBHPS.js → chunk-W66AZIOH.js} +2 -2
- package/dist/chunk-W66AZIOH.js.map +1 -0
- package/dist/{chunk-NZ655MWE.js → chunk-YEHO6FDW.js} +5 -4
- package/dist/chunk-YEHO6FDW.js.map +1 -0
- package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
- package/dist/chunk-YNUBMSMV.js.map +1 -0
- package/dist/{chunk-74C6SNEC.js → chunk-ZPK5656W.js} +3 -3
- package/dist/{chunk-74C6SNEC.js.map → chunk-ZPK5656W.js.map} +1 -1
- package/dist/components.d.ts +22 -899
- package/dist/components.js +436 -3118
- package/dist/components.js.map +1 -1
- package/dist/file-reference-9xUOnwyt.d.ts +70 -0
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +10 -10
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +49 -9
- package/dist/index.js +190 -25
- package/dist/index.js.map +1 -1
- package/dist/{organisation-CO3Sh3_D.d.ts → organisation-t-vvQC3g.d.ts} +1 -8
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +5 -5
- package/dist/rbac/index.d.ts +65 -46
- package/dist/rbac/index.js +10 -12
- package/dist/styles/core.css +0 -125
- package/dist/types.d.ts +2 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/dist/{usePublicRouteParams-B2OcAsur.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +1 -1
- package/dist/utils.d.ts +3 -4
- package/dist/utils.js +44 -13
- package/dist/utils.js.map +1 -1
- package/docs/FILE_REFERENCE_SYSTEM.md +440 -0
- package/docs/INDEX.md +7 -5
- package/docs/README.md +0 -1
- package/docs/api/README.md +0 -4
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +2 -2
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +12 -12
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +6 -6
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +281 -0
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +204 -200
- package/docs/api-reference/components.md +141 -163
- package/docs/api-reference/hooks.md +347 -0
- package/docs/core-concepts/rbac-system.md +69 -16
- package/docs/getting-started/examples/basic-auth-app.md +0 -1
- package/docs/implementation-guides/datatable-rbac-usage.md +12 -11
- package/docs/implementation-guides/file-upload-storage.md +733 -0
- package/docs/implementation-guides/inactivity-tracking.md +779 -0
- package/docs/implementation-guides/organisation-security.md +748 -0
- package/docs/implementation-guides/public-pages-advanced.md +1022 -0
- package/docs/migration/MIGRATION_GUIDE.md +684 -0
- package/docs/migration/README.md +13 -2
- package/docs/migration/rbac-migration.md +73 -0
- package/docs/rbac/examples/rbac-rls-integration-example.md +11 -13
- package/docs/style-guide.md +269 -1
- package/package.json +1 -1
- package/src/__tests__/TESTING_GUIDELINES.md +331 -18
- package/src/__tests__/helpers/supabaseMock.ts +99 -0
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -7
- package/src/__tests__/shared.ts +6 -0
- package/src/components/DataTable/components/ActionButtons.tsx +2 -2
- package/src/components/DataTable/components/DataTableCore.tsx +2 -2
- package/src/components/DataTable/components/UnifiedTableBody.tsx +1 -1
- package/src/components/DataTable/utils/debugTools.ts +2 -2
- package/src/components/Dialog/Dialog.test.tsx +12 -2
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +6 -6
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +2 -2
- package/src/components/FileDisplay.tsx +233 -0
- package/src/components/FileUpload.tsx +176 -0
- package/src/components/Footer/Footer.test.tsx +7 -7
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +13 -6
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +30 -3
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +558 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
- package/src/components/PublicLayout/PublicPageDebugger.tsx +2 -2
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +2 -2
- package/src/components/PublicLayout/PublicPageProvider.tsx +2 -2
- package/src/components/Select/Select.test.tsx +50 -15
- package/src/components/SuperAdminGuard.tsx +2 -2
- package/src/components/__tests__/SuperAdminGuard.test.tsx +559 -0
- package/src/components/index.ts +0 -183
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +1 -1
- package/src/hooks/__tests__/useRBAC.unit.test.ts +191 -138
- package/src/hooks/public/usePublicEvent.ts +2 -2
- package/src/hooks/useAppConfig.ts +3 -3
- package/src/hooks/useComponentPerformance.ts +1 -1
- package/src/hooks/useDataTablePerformance.ts +1 -1
- package/src/hooks/useFileReference.ts +232 -0
- package/src/hooks/useOrganisationPermissions.test.ts +254 -344
- package/src/hooks/useOrganisationPermissions.ts +15 -7
- package/src/hooks/useOrganisationSecurity.test.ts +390 -402
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePermissionCache.test.ts +264 -395
- package/src/hooks/usePermissionCache.ts +34 -4
- package/src/hooks/useSecureDataAccess.test.ts +486 -0
- package/src/hooks/useSecureDataAccess.ts +4 -1
- package/src/providers/InactivityProvider.tsx +2 -2
- package/src/providers/OrganisationProvider.test.simple.tsx +168 -0
- package/src/providers/OrganisationProvider.test.tsx +168 -0
- package/src/providers/OrganisationProvider.tsx +25 -31
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +205 -0
- package/src/providers/UnifiedAuthProvider.test.tsx +128 -0
- package/src/providers/__tests__/InactivityProvider.test.tsx +3 -4
- package/src/providers/__tests__/OrganisationProvider.test.tsx +19 -14
- package/src/rbac/__tests__/integration.authflow.test.tsx +123 -0
- package/src/rbac/__tests__/integration.navigation.test.tsx +72 -0
- package/src/rbac/__tests__/integration.securedata.test.tsx +92 -0
- package/src/rbac/__tests__/integration.smoke.test.tsx +73 -0
- package/src/rbac/__tests__/rbac-core.test.tsx +26 -22
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +411 -0
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +285 -0
- package/src/rbac/__tests__/rbac-functions.test.ts +655 -0
- package/src/rbac/__tests__/rbac-integration.test.ts +532 -0
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +196 -0
- package/src/rbac/api.test.ts +6 -6
- package/src/rbac/api.ts +2 -2
- package/src/rbac/audit.test.ts +485 -0
- package/src/rbac/audit.ts +7 -1
- package/src/rbac/cache-invalidation.ts +318 -0
- package/src/rbac/cache.test.ts +286 -0
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +559 -0
- package/src/rbac/components/EnhancedNavigationMenu.tsx +29 -23
- package/src/rbac/components/NavigationProvider.test.tsx +449 -0
- package/src/rbac/components/PagePermissionGuard.tsx +4 -4
- package/src/rbac/components/PagePermissionProvider.test.tsx +479 -0
- package/src/rbac/components/SecureDataProvider.test.tsx +511 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +159 -430
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +4 -5
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +112 -118
- package/src/rbac/config.test.ts +410 -0
- package/src/rbac/engine.test.simple.ts +237 -0
- package/src/rbac/engine.test.ts +233 -0
- package/src/rbac/engine.ts +37 -41
- package/src/rbac/examples/CompleteRBACExample.tsx +3 -3
- package/src/rbac/examples/EventBasedApp.tsx +4 -4
- package/src/rbac/hooks/useRBAC.simple.test.ts +16 -0
- package/src/rbac/hooks/useRBAC.test.ts +207 -455
- package/src/rbac/hooks/useRBAC.ts +30 -22
- package/src/rbac/permissions.test.ts +128 -0
- package/src/rbac/permissions.ts +56 -141
- package/src/rbac/providers/RBACProvider.tsx +1 -1
- package/src/rbac/secureClient.test.ts +444 -0
- package/src/rbac/security.test.ts +390 -0
- package/src/rbac/security.ts +1 -1
- package/src/rbac/types.test.ts +382 -0
- package/src/rbac/types.ts +2 -2
- package/src/styles/core.css +0 -125
- package/src/types/file-reference.ts +77 -0
- package/src/types/rbac-functions.ts +290 -0
- package/src/types/supabase.ts +10 -28
- package/src/types/unified.ts +4 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +81 -55
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +21 -12
- package/src/utils/__tests__/organisationContext.unit.test.ts +13 -7
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +3 -3
- package/src/utils/__tests__/sessionTracking.unit.test.ts +32 -12
- package/src/utils/appConfig.ts +1 -1
- package/src/utils/appIdResolver.test.ts +503 -0
- package/src/utils/appIdResolver.ts +1 -1
- package/src/utils/appNameResolver.test.ts +494 -0
- package/src/utils/appNameResolver.ts +3 -2
- package/src/utils/bundleAnalysis.ts +3 -3
- package/src/utils/debugLogger.ts +1 -1
- package/src/utils/file-reference.ts +263 -0
- package/src/utils/formatDate.test.ts +2 -2
- package/src/utils/organisationContext.test.ts +340 -0
- package/src/utils/organisationContext.ts +19 -6
- package/src/utils/performanceBudgets.ts +2 -2
- package/src/utils/permissionUtils.test.ts +393 -0
- package/src/utils/permissionUtils.ts +5 -2
- package/src/utils/secureDataAccess.test.ts +715 -0
- package/src/utils/secureDataAccess.ts +21 -5
- package/src/utils/sessionTracking.ts +34 -4
- package/src/utils/storage/__tests__/helpers.unit.test.ts +328 -0
- package/src/utils/storage/__tests__/index.unit.test.ts +16 -0
- package/src/utils/storage/helpers.ts +20 -25
- package/src/utils/storage/index.ts +29 -1
- package/src/vite-env.d.ts +17 -0
- package/dist/chunk-22KLBHPS.js.map +0 -1
- package/dist/chunk-7BNPOCLL.js.map +0 -1
- package/dist/chunk-BC3S53OZ.js.map +0 -1
- package/dist/chunk-GWSBHC4J.js.map +0 -1
- package/dist/chunk-MYP2EGHX.js.map +0 -1
- package/dist/chunk-MZBUOP4P.js.map +0 -1
- package/dist/chunk-NYUJ4FJR.js.map +0 -1
- package/dist/chunk-NZ655MWE.js.map +0 -1
- package/dist/chunk-SS3E6QLB.js.map +0 -1
- package/dist/chunk-TRIZ7IB7.js.map +0 -1
- package/dist/chunk-WJARTBCT.js.map +0 -1
- package/dist/chunk-YDJW5XTN.js.map +0 -1
- package/docs/print-components/README.md +0 -258
- package/docs/print-components/api-reference.md +0 -636
- package/docs/print-components/examples/README.md +0 -204
- package/docs/print-components/examples/basic-report.tsx +0 -92
- package/docs/print-components/examples/card-catalog.tsx +0 -149
- package/docs/print-components/examples/cover-page-report.tsx +0 -163
- package/docs/print-components/quick-start.md +0 -363
- package/src/components/PrintButton/PrintButton.tsx +0 -321
- package/src/components/PrintButton/PrintButtonGroup.tsx +0 -84
- package/src/components/PrintButton/PrintToolbar.tsx +0 -94
- package/src/components/PrintButton/__tests__/PrintButton.test.tsx +0 -271
- package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +0 -438
- package/src/components/PrintButton/index.ts +0 -33
- package/src/components/PrintButton/types.ts +0 -173
- package/src/components/PrintCard/PrintCard.tsx +0 -154
- package/src/components/PrintCard/PrintCardContent.tsx +0 -57
- package/src/components/PrintCard/PrintCardFooter.tsx +0 -60
- package/src/components/PrintCard/PrintCardGrid.tsx +0 -91
- package/src/components/PrintCard/PrintCardHeader.tsx +0 -78
- package/src/components/PrintCard/PrintCardImage.tsx +0 -81
- package/src/components/PrintCard/examples/PrintCardShowcase.tsx +0 -239
- package/src/components/PrintCard/index.ts +0 -34
- package/src/components/PrintCard/types.ts +0 -171
- package/src/components/PrintDataTable/PrintDataTable.tsx +0 -215
- package/src/components/PrintDataTable/PrintTableGroup.tsx +0 -90
- package/src/components/PrintDataTable/PrintTableRow.tsx +0 -76
- package/src/components/PrintDataTable/index.ts +0 -25
- package/src/components/PrintDataTable/types.ts +0 -67
- package/src/components/PrintFooter/PrintFooter.tsx +0 -183
- package/src/components/PrintFooter/PrintFooterContent.tsx +0 -71
- package/src/components/PrintFooter/PrintFooterInfo.tsx +0 -86
- package/src/components/PrintFooter/PrintPageNumber.tsx +0 -90
- package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +0 -390
- package/src/components/PrintFooter/index.ts +0 -30
- package/src/components/PrintFooter/types.ts +0 -149
- package/src/components/PrintGrid/PrintGrid.tsx +0 -180
- package/src/components/PrintGrid/PrintGridBreakpoint.tsx +0 -109
- package/src/components/PrintGrid/PrintGridContainer.tsx +0 -128
- package/src/components/PrintGrid/PrintGridItem.tsx +0 -220
- package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +0 -359
- package/src/components/PrintGrid/index.ts +0 -31
- package/src/components/PrintGrid/types.ts +0 -159
- package/src/components/PrintHeader/PrintCoverHeader.tsx +0 -230
- package/src/components/PrintHeader/PrintHeader.tsx +0 -150
- package/src/components/PrintHeader/index.ts +0 -17
- package/src/components/PrintHeader/types.ts +0 -42
- package/src/components/PrintLayout/PrintLayout.tsx +0 -122
- package/src/components/PrintLayout/PrintLayoutContext.tsx +0 -66
- package/src/components/PrintLayout/PrintPageBreak.tsx +0 -52
- package/src/components/PrintLayout/examples/PrintShowcase.tsx +0 -230
- package/src/components/PrintLayout/index.ts +0 -19
- package/src/components/PrintLayout/types.ts +0 -37
- package/src/components/PrintPageBreak/PrintPageBreak.tsx +0 -120
- package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +0 -90
- package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +0 -112
- package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +0 -279
- package/src/components/PrintPageBreak/index.ts +0 -23
- package/src/components/PrintPageBreak/types.ts +0 -94
- package/src/components/PrintSection/PrintColumn.tsx +0 -104
- package/src/components/PrintSection/PrintDivider.tsx +0 -101
- package/src/components/PrintSection/PrintSection.tsx +0 -129
- package/src/components/PrintSection/PrintSectionContent.tsx +0 -75
- package/src/components/PrintSection/PrintSectionHeader.tsx +0 -97
- package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +0 -258
- package/src/components/PrintSection/index.ts +0 -33
- package/src/components/PrintSection/types.ts +0 -155
- package/src/components/PrintText/PrintText.tsx +0 -116
- package/src/components/PrintText/index.ts +0 -16
- package/src/components/PrintText/types.ts +0 -24
- package/src/rbac/__tests__/integration.test.tsx +0 -218
- package/src/utils/print/PrintDataProcessor.ts +0 -390
- package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +0 -397
- package/src/utils/print/index.ts +0 -29
- package/src/utils/print/types.ts +0 -196
- package/src/utils/print/usePrintOptimization.ts +0 -272
- /package/dist/{DataTable-7FMFXA7A.js.map → DataTable-DJQTKX33.js.map} +0 -0
- /package/dist/{api-H5A3H4IR.js.map → api-LUNF5O6M.js.map} +0 -0
- /package/dist/{appNameResolver-7GHF5ED2.js.map → appNameResolver-UURKN7NF.js.map} +0 -0
- /package/dist/{audit-BUW3LMJB.js.map → audit-6TOCAMKO.js.map} +0 -0
- /package/dist/{chunk-NRK4AIHQ.js.map → chunk-2DFCT6D3.js.map} +0 -0
- /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
|
+
});
|