@jmruthers/pace-core 0.5.67 → 0.5.69
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 +26 -0
- package/dist/{DataTable-MFUXNGPR.js → DataTable-MPBSXUC6.js} +5 -6
- package/dist/{PublicLoadingSpinner-DdKXTkCZ.d.ts → PublicLoadingSpinner-BOdyU3u-.d.ts} +1 -1
- package/dist/{UnifiedAuthProvider-CQNiemcB.d.ts → UnifiedAuthProvider-D02AMXgO.d.ts} +3 -3
- package/dist/{chunk-CKNY7HYS.js → chunk-2ARQW6VX.js} +3 -3
- package/dist/{chunk-T2MQY57J.js → chunk-6JILXFEA.js} +335 -5
- package/dist/chunk-6JILXFEA.js.map +1 -0
- package/dist/{chunk-D7ARGIA3.js → chunk-6RBH67W7.js} +23 -6
- package/dist/chunk-6RBH67W7.js.map +1 -0
- package/dist/{chunk-C7GUF747.js → chunk-FJTAWPAQ.js} +3 -5
- package/dist/{chunk-C7GUF747.js.map → chunk-FJTAWPAQ.js.map} +1 -1
- package/dist/{chunk-4HQ5BOVZ.js → chunk-NO5QHMDX.js} +7 -6
- package/dist/chunk-NO5QHMDX.js.map +1 -0
- package/dist/{chunk-ZPK5656W.js → chunk-O3NWNXDY.js} +4 -5
- package/dist/chunk-O3NWNXDY.js.map +1 -0
- package/dist/{chunk-BTCA3ENN.js → chunk-Q2UP3ZWQ.js} +4 -4
- package/dist/{chunk-QVEOQVD4.js → chunk-RVYGJPOD.js} +173 -20
- package/dist/chunk-RVYGJPOD.js.map +1 -0
- package/dist/{chunk-FVDOEGGG.js → chunk-UCMHBF7Y.js} +3 -5
- package/dist/{chunk-FVDOEGGG.js.map → chunk-UCMHBF7Y.js.map} +1 -1
- package/dist/{chunk-T6HVDA24.js → chunk-V3QO3LL7.js} +5 -7
- package/dist/chunk-V3QO3LL7.js.map +1 -0
- package/dist/{chunk-ZB6AEA7I.js → chunk-ZXJGZLLO.js} +17 -17
- package/dist/{chunk-ZB6AEA7I.js.map → chunk-ZXJGZLLO.js.map} +1 -1
- package/dist/components.d.ts +2 -2
- package/dist/components.js +8 -9
- package/dist/components.js.map +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +9 -6
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +16 -16
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +5 -7
- package/dist/rbac/index.js +5 -6
- package/dist/{usePublicRouteParams-CdoFxnJK.d.ts → usePublicRouteParams-Ua1Vz-HG.d.ts} +35 -1
- package/dist/utils.d.ts +4 -1
- package/dist/utils.js +3 -3
- package/docs/DOCUMENTATION_CHECKLIST.md +281 -0
- package/docs/README.md +22 -10
- package/docs/api/README.md +26 -0
- package/docs/api/classes/ColumnFactory.md +1 -1
- 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 +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +2 -2
- 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 +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- 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/SwitchProps.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 +4 -4
- 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 +39 -14
- package/docs/api-reference/providers.md +16 -7
- package/docs/architecture/services.md +374 -0
- package/docs/best-practices/README.md +1 -1
- package/docs/best-practices/testing.md +1 -1
- package/docs/breaking-changes.md +182 -0
- package/docs/common-patterns.md +445 -0
- package/docs/core-concepts/authentication.md +26 -11
- package/docs/core-concepts/events.md +2 -0
- package/docs/core-concepts/organisations.md +2 -0
- package/docs/core-concepts/permissions.md +2 -0
- package/docs/{INDEX.md → documentation-index.md} +26 -38
- package/docs/faq.md +286 -0
- package/docs/{FILE_REFERENCE_SYSTEM.md → file-reference-system.md} +1 -1
- package/docs/getting-started/installation-guide.md +284 -0
- package/docs/getting-started/quick-start.md +8 -1
- package/docs/implementation-guides/app-layout.md +3 -1
- package/docs/implementation-guides/data-tables.md +2 -0
- package/docs/implementation-guides/dynamic-colors.md +47 -2
- package/docs/implementation-guides/event-theming-summary.md +220 -0
- package/docs/implementation-guides/forms.md +9 -7
- package/docs/implementation-guides/navigation.md +2 -0
- package/docs/migration/service-architecture.md +351 -0
- package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +226 -0
- package/docs/rbac/README-rbac-rls-integration.md +2 -2
- package/docs/rbac/README.md +1 -1
- package/docs/rbac/examples/rbac-rls-integration-example.md +3 -3
- package/docs/rbac/quick-start.md +2 -0
- package/docs/rbac/rbac-rls-integration.md +2 -2
- package/docs/security/README.md +5 -1
- package/docs/style-guide.md +136 -1
- package/docs/testing/README.md +1 -1
- package/docs/troubleshooting/authentication-issues.md +334 -0
- package/docs/troubleshooting/common-issues.md +2 -0
- package/docs/troubleshooting/styling-issues.md +199 -144
- package/docs/usage.md +23 -2
- package/package.json +1 -1
- package/src/__tests__/{TESTING_GUIDELINES.md → TEST_GUIDE_CURSOR.md} +20 -0
- package/src/__tests__/TEST_GUIDE_HUMAN.md +103 -0
- package/src/__tests__/fixtures/test-data.ts +90 -0
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +260 -0
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +224 -0
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +273 -0
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +98 -0
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +436 -0
- package/src/__tests__/helpers/__tests__/timer-utils.test.ts +371 -0
- package/src/__tests__/helpers/component-test-utils.tsx +14 -4
- package/src/__tests__/helpers/optimized-test-setup.ts +68 -0
- package/src/__tests__/helpers/test-providers.tsx +329 -0
- package/src/__tests__/helpers/test-utils.tsx +91 -45
- package/src/__tests__/helpers/timer-utils.ts +71 -0
- package/src/__tests__/hooks/usePermissions.test.ts +1 -5
- package/src/__tests__/integration/UserProfile.test.tsx +1 -5
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +42 -12
- package/src/__tests__/setup.ts +34 -28
- package/src/components/Alert/Alert.test.tsx +1 -5
- package/src/components/Avatar/Avatar.test.tsx +1 -5
- package/src/components/Button/Button.test.tsx +4 -20
- package/src/components/Card/Card.test.tsx +1 -5
- package/src/components/Checkbox/Checkbox.test.tsx +1 -5
- package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +1 -5
- package/src/components/DataTable/__tests__/DataTable.test.tsx +45 -49
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +1 -5
- package/src/components/DataTable/__tests__/styles.test.ts +382 -0
- package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +409 -0
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +634 -0
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +519 -0
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +714 -0
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +592 -0
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +354 -0
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +539 -0
- package/src/components/Dialog/examples/__tests__/SmartDialogExample.unit.test.tsx +1 -5
- package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +1 -8
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +34 -38
- package/src/components/Footer/Footer.test.tsx +1 -5
- package/src/components/Form/Form.test.tsx +22 -35
- package/src/components/Header/Header.test.tsx +1 -9
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +1 -5
- package/src/components/Input/Input.test.tsx +2 -10
- package/src/components/LoginForm/LoginForm.test.tsx +1 -5
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +24 -24
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +1 -6
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +6 -16
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +1 -5
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +1 -5
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +1 -7
- package/src/components/PasswordReset/PasswordChangeForm.test.tsx +1 -9
- package/src/components/PasswordReset/PasswordResetForm.test.tsx +1 -9
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +4 -5
- package/src/components/PublicLayout/PublicPageHeader.tsx +13 -9
- package/src/components/PublicLayout/__tests__/EventLogo.test.tsx +666 -0
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +457 -0
- package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +393 -0
- package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +351 -0
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +374 -0
- package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +388 -0
- package/src/components/Select/Select.bug-test.tsx +69 -0
- package/src/components/Select/Select.refactored.tsx +497 -0
- package/src/components/Select/Select.test.tsx +42 -49
- package/src/components/Select/Select.tsx +5 -2
- package/src/components/Select/hooks.ts +254 -0
- package/src/components/Switch/Switch.test.tsx +1 -5
- package/src/components/Table/__tests__/Table.test.tsx +775 -0
- package/src/components/Toast/Toast.test.tsx +15 -8
- package/src/components/Tooltip/Tooltip.test.tsx +1 -5
- package/src/components/UserMenu/UserMenu.test.tsx +3 -15
- package/src/components/__tests__/FileDisplay.test.tsx +575 -0
- package/src/components/__tests__/FileUpload.test.tsx +446 -0
- package/src/components/__tests__/SuperAdminGuard.test.tsx +422 -354
- package/src/hooks/__tests__/ServiceHooks.test.tsx +613 -0
- package/src/hooks/__tests__/hooks.integration.test.tsx +1 -10
- package/src/hooks/__tests__/useApiFetch.unit.test.ts +10 -14
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +307 -0
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +1 -6
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +1 -5
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +6 -9
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +321 -0
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +583 -0
- package/src/hooks/__tests__/usePublicEventLogo.unit.test.ts +640 -0
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +435 -0
- package/src/hooks/__tests__/useRBAC.unit.test.ts +10 -10
- package/src/hooks/__tests__/useStorage.unit.test.ts +751 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/public/usePublicEvent.ts +181 -13
- package/src/hooks/public/usePublicRouteParams.ts +13 -3
- package/src/hooks/services/useAuth.ts +50 -0
- package/src/hooks/services/useAuthService.ts +30 -0
- package/src/hooks/services/useCurrentEvent.ts +36 -0
- package/src/hooks/services/useCurrentOrganisation.ts +52 -0
- package/src/hooks/services/useEventService.ts +30 -0
- package/src/hooks/services/useInactivityService.ts +30 -0
- package/src/hooks/services/useOrganisationService.ts +30 -0
- package/src/hooks/services/usePermissions.ts +70 -0
- package/src/hooks/services/useRBACService.ts +30 -0
- package/src/hooks/useCounter.test.ts +1 -5
- package/src/hooks/useEventTheme.ts +86 -0
- package/src/hooks/useOrganisationPermissions.test.ts +2 -5
- package/src/hooks/useOrganisationSecurity.test.ts +1 -5
- package/src/hooks/usePermissionCache.test.ts +1 -5
- package/src/hooks/usePermissionCheck.ts +150 -0
- package/src/hooks/useSecureDataAccess.test.ts +1 -5
- package/src/index.ts +1 -0
- package/src/providers/OrganisationProvider.test.tsx +1 -5
- package/src/providers/OrganisationProvider.tsx +56 -4
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +42 -6
- package/src/providers/UnifiedAuthProvider.test.tsx +1 -5
- package/src/providers/UnifiedAuthProvider.tsx +4 -4
- package/src/providers/__tests__/AuthProvider.test.tsx +105 -439
- package/src/providers/__tests__/AuthProvider.test.tsx.backup +771 -0
- package/src/providers/__tests__/EventProvider.test.tsx +211 -110
- package/src/providers/__tests__/EventProvider.test.tsx.backup +824 -0
- package/src/providers/__tests__/InactivityProvider.test.tsx +1 -5
- package/src/providers/__tests__/OrganisationProvider.test.tsx +97 -261
- package/src/providers/__tests__/OrganisationProvider.test.tsx.backup +820 -0
- package/src/providers/__tests__/ServiceProviders.test.tsx +477 -0
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +72 -504
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx.backup +911 -0
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx.backup2 +166 -0
- package/src/providers/services/AuthServiceProvider.tsx +65 -0
- package/src/providers/services/EventServiceProvider.tsx +83 -0
- package/src/providers/services/InactivityServiceProvider.tsx +83 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +77 -0
- package/src/providers/services/RBACServiceProvider.tsx +79 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +368 -0
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +210 -0
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +269 -0
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +892 -0
- package/src/rbac/__tests__/engine.comprehensive.test.ts +954 -0
- package/src/rbac/__tests__/integration.authflow.test.tsx +1 -5
- package/src/rbac/__tests__/integration.navigation.test.tsx +1 -4
- package/src/rbac/__tests__/rbac-core.test.tsx +2 -7
- package/src/rbac/__tests__/rbac-functions.test.ts +1 -9
- package/src/rbac/__tests__/rbac-integration.test.ts +1 -9
- package/src/rbac/api.test.ts +1 -9
- package/src/rbac/cache.test.ts +10 -8
- package/src/rbac/cli/__tests__/policy-manager.test.ts +339 -0
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +1 -5
- package/src/rbac/components/NavigationProvider.test.tsx +1 -5
- package/src/rbac/components/PagePermissionProvider.test.tsx +1 -5
- package/src/rbac/components/SecureDataProvider.test.tsx +1 -5
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +25 -29
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +27 -30
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +23 -27
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +18 -22
- package/src/rbac/config.test.ts +1 -5
- package/src/rbac/hooks/useCan.test.ts +262 -9
- package/src/rbac/hooks/usePermissions.test.ts +246 -6
- package/src/rbac/hooks/useRBAC.simple.test.ts +1 -5
- package/src/rbac/hooks/useRBAC.test.ts +472 -198
- package/src/rbac/providers/__tests__/RBACProvider.test.tsx +1 -9
- package/src/services/AuthService.ts +416 -0
- package/src/services/EventService.ts +366 -0
- package/src/services/InactivityService.ts +388 -0
- package/src/services/OrganisationService.ts +592 -0
- package/src/services/RBACService.ts +522 -0
- package/src/services/__tests__/AuthService.test.ts +356 -0
- package/src/services/__tests__/BaseService.test.ts +314 -0
- package/src/services/__tests__/EventService.test.ts +489 -0
- package/src/services/__tests__/InactivityService.test.ts +403 -0
- package/src/services/__tests__/OrganisationService.test.ts +660 -0
- package/src/services/__tests__/RBACService.test.ts +492 -0
- package/src/services/base/BaseService.ts +87 -0
- package/src/services/interfaces/IAuthService.ts +39 -0
- package/src/services/interfaces/IEventService.ts +30 -0
- package/src/services/interfaces/IInactivityService.ts +31 -0
- package/src/services/interfaces/IOrganisationService.ts +41 -0
- package/src/services/interfaces/IRBACService.ts +62 -0
- package/src/theming/__tests__/runtime.test.ts +540 -0
- package/src/types/__tests__/file-reference.test.ts +447 -0
- package/src/types/__tests__/organisation.test.ts +1133 -0
- package/src/types/__tests__/theme.test.ts +830 -0
- package/src/types/__tests__/type-validation.test.ts +527 -0
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +1 -5
- package/src/utils/__tests__/debugLogger.test.ts +417 -0
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +1 -6
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +1 -5
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +35 -35
- package/src/utils/__tests__/organisationContext.unit.test.ts +1 -5
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +5 -11
- package/src/utils/__tests__/secureErrors.unit.test.ts +1 -6
- package/src/utils/__tests__/secureStorage.unit.test.ts +1 -5
- package/src/utils/__tests__/securityMonitor.unit.test.ts +1 -5
- package/src/utils/__tests__/sessionTracking.unit.test.ts +1 -5
- package/src/utils/appIdResolver.test.ts +6 -10
- package/src/utils/appNameResolver.simple.test.ts +142 -0
- package/src/utils/appNameResolver.test.ts +31 -458
- package/src/utils/appNameResolver.test.ts.backup +494 -0
- package/src/utils/debugLogger.ts +26 -5
- package/src/utils/formatDate.test.ts +1 -5
- package/src/utils/organisationContext.test.ts +1 -5
- package/src/utils/performanceBudgets.ts +3 -4
- package/src/utils/secureDataAccess.test.ts +1 -5
- package/src/utils/storage/__tests__/helpers.unit.test.ts +1 -5
- package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +1 -5
- package/dist/chunk-4HQ5BOVZ.js.map +0 -1
- package/dist/chunk-D7ARGIA3.js.map +0 -1
- package/dist/chunk-QVEOQVD4.js.map +0 -1
- package/dist/chunk-T2MQY57J.js.map +0 -1
- package/dist/chunk-T6HVDA24.js.map +0 -1
- package/dist/chunk-VTJ5HCZB.js +0 -315
- package/dist/chunk-VTJ5HCZB.js.map +0 -1
- package/dist/chunk-ZPK5656W.js.map +0 -1
- package/docs/getting-started/installation.md +0 -269
- package/src/__tests__/REBUILD_PLAN.md +0 -223
- /package/dist/{DataTable-MFUXNGPR.js.map → DataTable-MPBSXUC6.js.map} +0 -0
- /package/dist/{chunk-CKNY7HYS.js.map → chunk-2ARQW6VX.js.map} +0 -0
- /package/dist/{chunk-BTCA3ENN.js.map → chunk-Q2UP3ZWQ.js.map} +0 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file RBACService Unit Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Services/__tests__
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Unit tests for RBACService class.
|
|
8
|
+
* Tests role-based access control operations, permission checking, and state management.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
12
|
+
import { RBACService } from '../RBACService';
|
|
13
|
+
import { AccessLevel } from '../../types/unified';
|
|
14
|
+
|
|
15
|
+
// Mock Supabase client
|
|
16
|
+
const createMockSupabaseClient = () => ({
|
|
17
|
+
rpc: vi.fn(),
|
|
18
|
+
from: vi.fn(() => ({
|
|
19
|
+
select: vi.fn(() => ({
|
|
20
|
+
eq: vi.fn(() => ({
|
|
21
|
+
eq: vi.fn(() => ({
|
|
22
|
+
single: vi.fn()
|
|
23
|
+
}))
|
|
24
|
+
}))
|
|
25
|
+
}))
|
|
26
|
+
})),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Mock user and session
|
|
30
|
+
const mockUser = {
|
|
31
|
+
id: 'user-1',
|
|
32
|
+
email: 'test@example.com',
|
|
33
|
+
user_metadata: { globalRole: 'user' }
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const mockSession = {
|
|
37
|
+
access_token: 'token',
|
|
38
|
+
user: mockUser
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
describe('RBACService', () => {
|
|
42
|
+
let mockSupabase: ReturnType<typeof createMockSupabaseClient>;
|
|
43
|
+
let rbacService: RBACService;
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
mockSupabase = createMockSupabaseClient();
|
|
47
|
+
rbacService = new RBACService(mockSupabase as any, mockUser, mockSession, 'test-app');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
rbacService.cleanup();
|
|
52
|
+
vi.clearAllMocks();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('Initialization', () => {
|
|
56
|
+
it('should initialize with default state', () => {
|
|
57
|
+
expect(rbacService.getPermissions()).toEqual({});
|
|
58
|
+
expect(rbacService.getRoles()).toEqual([]);
|
|
59
|
+
expect(rbacService.getAccessLevel()).toBe(AccessLevel.VIEWER);
|
|
60
|
+
expect(rbacService.isLoading()).toBe(false);
|
|
61
|
+
expect(rbacService.getError()).toBeNull();
|
|
62
|
+
expect(rbacService.getSelectedEventId()).toBeNull();
|
|
63
|
+
expect(rbacService.getAppConfig()).toBeNull();
|
|
64
|
+
expect(rbacService.getUserEventAccess()).toEqual([]);
|
|
65
|
+
expect(rbacService.isEventAccessLoading()).toBe(false);
|
|
66
|
+
expect(rbacService.getSelectedOrganisationId()).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should set super admin role from user metadata', async () => {
|
|
70
|
+
const superAdminUser = {
|
|
71
|
+
...mockUser,
|
|
72
|
+
user_metadata: { globalRole: 'super_admin' }
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const service = new RBACService(mockSupabase as any, superAdminUser, mockSession, 'test-app');
|
|
76
|
+
await service.initialize();
|
|
77
|
+
|
|
78
|
+
expect(service.getRoles()).toContain('super_admin');
|
|
79
|
+
expect(service.getAccessLevel()).toBe(AccessLevel.ADMIN);
|
|
80
|
+
expect(service.getPermissions()).toHaveProperty('admin:create', true);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('Permission Checking', () => {
|
|
85
|
+
beforeEach(async () => {
|
|
86
|
+
// Mock app config loading
|
|
87
|
+
mockSupabase.from.mockReturnValue({
|
|
88
|
+
select: vi.fn().mockReturnValue({
|
|
89
|
+
eq: vi.fn().mockReturnValue({
|
|
90
|
+
eq: vi.fn().mockReturnValue({
|
|
91
|
+
single: vi.fn().mockResolvedValue({
|
|
92
|
+
data: { id: 'app-1' },
|
|
93
|
+
error: null
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Mock get_app_config RPC
|
|
101
|
+
mockSupabase.rpc.mockImplementation((rpcName, params) => {
|
|
102
|
+
if (rpcName === 'get_app_config') {
|
|
103
|
+
return Promise.resolve({
|
|
104
|
+
data: [{
|
|
105
|
+
supports_direct_access: false,
|
|
106
|
+
requires_event: true
|
|
107
|
+
}],
|
|
108
|
+
error: null
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// Mock rbac_permissions_get for permission checking
|
|
112
|
+
return Promise.resolve({
|
|
113
|
+
data: [
|
|
114
|
+
{
|
|
115
|
+
permission_type: 'event_app_access',
|
|
116
|
+
role_name: 'planner',
|
|
117
|
+
permission: 'events:read'
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
error: null
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Set event ID before initialization so permissions are loaded
|
|
125
|
+
rbacService.setSelectedEventId('event-1');
|
|
126
|
+
await rbacService.initialize();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should check permissions correctly', async () => {
|
|
130
|
+
expect(rbacService.hasPermission('events:read')).toBe(true);
|
|
131
|
+
expect(rbacService.hasPermission('events:write')).toBe(false);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should check roles correctly', async () => {
|
|
135
|
+
expect(rbacService.hasRole('planner')).toBe(true);
|
|
136
|
+
expect(rbacService.hasRole('admin')).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should check access levels correctly', async () => {
|
|
140
|
+
expect(rbacService.hasAccessLevel(AccessLevel.VIEWER)).toBe(true);
|
|
141
|
+
expect(rbacService.hasAccessLevel(AccessLevel.PLANNER)).toBe(true);
|
|
142
|
+
expect(rbacService.hasAccessLevel(AccessLevel.ADMIN)).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should check resource access correctly', async () => {
|
|
146
|
+
expect(rbacService.canAccess('events', 'read')).toBe(true);
|
|
147
|
+
expect(rbacService.canAccess('events', 'write')).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should validate permissions asynchronously', async () => {
|
|
151
|
+
const result = await rbacService.validatePermission('events:read');
|
|
152
|
+
expect(result).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should validate access asynchronously', async () => {
|
|
156
|
+
const result = await rbacService.validateAccess('events', 'read');
|
|
157
|
+
expect(result).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('Event Management', () => {
|
|
162
|
+
it('should set selected event ID', () => {
|
|
163
|
+
rbacService.setSelectedEventId('event-123');
|
|
164
|
+
|
|
165
|
+
expect(rbacService.getSelectedEventId()).toBe('event-123');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should get user event access for specific event', async () => {
|
|
169
|
+
const mockEventAccess = [
|
|
170
|
+
{
|
|
171
|
+
event_id: 'event-1',
|
|
172
|
+
event_name: 'Test Event',
|
|
173
|
+
event_description: 'Test Description',
|
|
174
|
+
start_date: '2024-01-01',
|
|
175
|
+
end_date: '2024-01-02',
|
|
176
|
+
event_status: 'active',
|
|
177
|
+
app_id: 'app-1',
|
|
178
|
+
access_level: 'planner',
|
|
179
|
+
granted_at: '2024-01-01T00:00:00Z',
|
|
180
|
+
organisation_id: 'org-1'
|
|
181
|
+
}
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
// Mock the rbac_apps and rbac_event_app_roles queries
|
|
185
|
+
mockSupabase.from.mockImplementation((table: string) => {
|
|
186
|
+
if (table === 'rbac_apps') {
|
|
187
|
+
return {
|
|
188
|
+
select: vi.fn().mockReturnValue({
|
|
189
|
+
eq: vi.fn().mockReturnValue({
|
|
190
|
+
eq: vi.fn().mockReturnValue({
|
|
191
|
+
single: vi.fn().mockResolvedValue({
|
|
192
|
+
data: { id: 'app-1' },
|
|
193
|
+
error: null
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
if (table === 'rbac_event_app_roles') {
|
|
201
|
+
return {
|
|
202
|
+
select: vi.fn().mockReturnValue({
|
|
203
|
+
eq: vi.fn().mockReturnValue({
|
|
204
|
+
eq: vi.fn().mockResolvedValue({
|
|
205
|
+
data: mockEventAccess,
|
|
206
|
+
error: null
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return mockSupabase.from();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
await rbacService.loadUserEventAccess();
|
|
216
|
+
|
|
217
|
+
const eventAccess = rbacService.getUserEventAccessById('event-1');
|
|
218
|
+
expect(eventAccess).toEqual(mockEventAccess[0]);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe('Organisation Context', () => {
|
|
223
|
+
it('should require organisation context', () => {
|
|
224
|
+
expect(() => rbacService.requireOrganisationContext()).toThrow('Organisation context is required but not available');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should return organisation context when set', () => {
|
|
228
|
+
rbacService.setSelectedEventId('event-1'); // This sets the organisation context internally
|
|
229
|
+
|
|
230
|
+
// Mock the organisation context
|
|
231
|
+
const orgId = 'org-1';
|
|
232
|
+
// Note: In a real implementation, this would be set by the OrganisationService
|
|
233
|
+
// For testing, we'll simulate it
|
|
234
|
+
(rbacService as any).selectedOrganisationId = orgId;
|
|
235
|
+
|
|
236
|
+
expect(rbacService.requireOrganisationContext()).toBe(orgId);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('Data Operations', () => {
|
|
241
|
+
it('should refresh permissions for event', async () => {
|
|
242
|
+
// Mock the rbac_apps query
|
|
243
|
+
mockSupabase.from.mockReturnValue({
|
|
244
|
+
select: vi.fn().mockReturnValue({
|
|
245
|
+
eq: vi.fn().mockReturnValue({
|
|
246
|
+
eq: vi.fn().mockReturnValue({
|
|
247
|
+
single: vi.fn().mockResolvedValue({
|
|
248
|
+
data: { id: 'app-1' },
|
|
249
|
+
error: null
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
mockSupabase.rpc.mockImplementation((rpcName, params) => {
|
|
257
|
+
if (rpcName === 'get_app_config') {
|
|
258
|
+
return Promise.resolve({
|
|
259
|
+
data: [{
|
|
260
|
+
supports_direct_access: false,
|
|
261
|
+
requires_event: true
|
|
262
|
+
}],
|
|
263
|
+
error: null
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return Promise.resolve({
|
|
267
|
+
data: [
|
|
268
|
+
{
|
|
269
|
+
permission_type: 'event_app_access',
|
|
270
|
+
role_name: 'planner',
|
|
271
|
+
permission: 'events:read'
|
|
272
|
+
}
|
|
273
|
+
],
|
|
274
|
+
error: null
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Ensure app config is loaded
|
|
279
|
+
await rbacService.initialize();
|
|
280
|
+
|
|
281
|
+
// Clear the RPC mock call count from initialization
|
|
282
|
+
vi.clearAllMocks();
|
|
283
|
+
|
|
284
|
+
// Re-setup mocks for the refresh call
|
|
285
|
+
mockSupabase.from.mockReturnValue({
|
|
286
|
+
select: vi.fn().mockReturnValue({
|
|
287
|
+
eq: vi.fn().mockReturnValue({
|
|
288
|
+
eq: vi.fn().mockReturnValue({
|
|
289
|
+
single: vi.fn().mockResolvedValue({
|
|
290
|
+
data: { id: 'app-1' },
|
|
291
|
+
error: null
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
})
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
mockSupabase.rpc.mockResolvedValue({
|
|
299
|
+
data: [
|
|
300
|
+
{
|
|
301
|
+
permission_type: 'event_app_access',
|
|
302
|
+
role_name: 'planner',
|
|
303
|
+
permission: 'events:read'
|
|
304
|
+
}
|
|
305
|
+
],
|
|
306
|
+
error: null
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
await rbacService.refreshPermissions('event-1');
|
|
310
|
+
|
|
311
|
+
expect(mockSupabase.rpc).toHaveBeenCalledWith('rbac_permissions_get', {
|
|
312
|
+
p_user_id: mockUser.id,
|
|
313
|
+
p_app_id: 'app-1',
|
|
314
|
+
p_event_id: 'event-1',
|
|
315
|
+
p_organisation_id: null
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('should load user event access', async () => {
|
|
320
|
+
const mockEventAccess = [
|
|
321
|
+
{
|
|
322
|
+
event_id: 'event-1',
|
|
323
|
+
role: 'planner',
|
|
324
|
+
granted_at: '2024-01-01T00:00:00Z'
|
|
325
|
+
}
|
|
326
|
+
];
|
|
327
|
+
|
|
328
|
+
// Mock the rbac_apps and rbac_event_app_roles queries
|
|
329
|
+
mockSupabase.from.mockImplementation((table: string) => {
|
|
330
|
+
if (table === 'rbac_apps') {
|
|
331
|
+
return {
|
|
332
|
+
select: vi.fn().mockReturnValue({
|
|
333
|
+
eq: vi.fn().mockReturnValue({
|
|
334
|
+
eq: vi.fn().mockReturnValue({
|
|
335
|
+
single: vi.fn().mockResolvedValue({
|
|
336
|
+
data: { id: 'app-1' },
|
|
337
|
+
error: null
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
})
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (table === 'rbac_event_app_roles') {
|
|
345
|
+
return {
|
|
346
|
+
select: vi.fn().mockReturnValue({
|
|
347
|
+
eq: vi.fn().mockReturnValue({
|
|
348
|
+
eq: vi.fn().mockResolvedValue({
|
|
349
|
+
data: mockEventAccess,
|
|
350
|
+
error: null
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
})
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return mockSupabase.from();
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
await rbacService.loadUserEventAccess();
|
|
360
|
+
|
|
361
|
+
expect(rbacService.getUserEventAccess()).toHaveLength(1);
|
|
362
|
+
expect(rbacService.getUserEventAccess()[0].event_id).toBe('event-1');
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should handle RPC errors gracefully', async () => {
|
|
366
|
+
// Mock the rbac_apps query
|
|
367
|
+
mockSupabase.from.mockReturnValue({
|
|
368
|
+
select: vi.fn().mockReturnValue({
|
|
369
|
+
eq: vi.fn().mockReturnValue({
|
|
370
|
+
eq: vi.fn().mockReturnValue({
|
|
371
|
+
single: vi.fn().mockResolvedValue({
|
|
372
|
+
data: { id: 'app-1' },
|
|
373
|
+
error: null
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
})
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Mock the RPC call to return app config first, then error
|
|
381
|
+
mockSupabase.rpc.mockImplementation((rpcName, params) => {
|
|
382
|
+
if (rpcName === 'get_app_config') {
|
|
383
|
+
return Promise.resolve({
|
|
384
|
+
data: [{
|
|
385
|
+
supports_direct_access: false,
|
|
386
|
+
requires_event: true
|
|
387
|
+
}],
|
|
388
|
+
error: null
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
if (rpcName === 'rbac_permissions_get') {
|
|
392
|
+
return Promise.resolve({
|
|
393
|
+
data: null,
|
|
394
|
+
error: { message: 'RPC error' }
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
return Promise.resolve({ data: null, error: null });
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// Ensure app config is loaded
|
|
401
|
+
await rbacService.initialize();
|
|
402
|
+
|
|
403
|
+
await rbacService.refreshPermissions('event-1');
|
|
404
|
+
|
|
405
|
+
expect(rbacService.getError()).toBeDefined();
|
|
406
|
+
expect(rbacService.getError()?.message).toBe('RPC error');
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
describe('State Management', () => {
|
|
411
|
+
it('should notify subscribers when state changes', () => {
|
|
412
|
+
const subscriber = vi.fn();
|
|
413
|
+
const unsubscribe = rbacService.subscribe(subscriber);
|
|
414
|
+
|
|
415
|
+
rbacService.setSelectedEventId('event-1');
|
|
416
|
+
|
|
417
|
+
expect(subscriber).toHaveBeenCalled();
|
|
418
|
+
|
|
419
|
+
unsubscribe();
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('should cleanup subscriptions on cleanup', () => {
|
|
423
|
+
const subscriber = vi.fn();
|
|
424
|
+
rbacService.subscribe(subscriber);
|
|
425
|
+
|
|
426
|
+
rbacService.cleanup();
|
|
427
|
+
|
|
428
|
+
// After cleanup, new state changes shouldn't notify subscribers
|
|
429
|
+
rbacService.setSelectedEventId('event-1');
|
|
430
|
+
|
|
431
|
+
expect(subscriber).not.toHaveBeenCalled();
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
describe('Super Admin Override', () => {
|
|
436
|
+
it('should grant all permissions to super admin', async () => {
|
|
437
|
+
const superAdminUser = {
|
|
438
|
+
...mockUser,
|
|
439
|
+
user_metadata: { globalRole: 'super_admin' }
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const service = new RBACService(mockSupabase as any, superAdminUser, mockSession, 'test-app');
|
|
443
|
+
await service.initialize();
|
|
444
|
+
|
|
445
|
+
expect(service.hasPermission('admin:create')).toBe(true);
|
|
446
|
+
expect(service.hasPermission('admin:read')).toBe(true);
|
|
447
|
+
expect(service.hasPermission('admin:update')).toBe(true);
|
|
448
|
+
expect(service.hasPermission('admin:delete')).toBe(true);
|
|
449
|
+
expect(service.hasPermission('users:create')).toBe(true);
|
|
450
|
+
expect(service.hasPermission('users:read')).toBe(true);
|
|
451
|
+
expect(service.hasPermission('users:update')).toBe(true);
|
|
452
|
+
expect(service.hasPermission('users:delete')).toBe(true);
|
|
453
|
+
expect(service.hasPermission('events:create')).toBe(true);
|
|
454
|
+
expect(service.hasPermission('events:read')).toBe(true);
|
|
455
|
+
expect(service.hasPermission('events:update')).toBe(true);
|
|
456
|
+
expect(service.hasPermission('events:delete')).toBe(true);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('should recognize super admin role', async () => {
|
|
460
|
+
const superAdminUser = {
|
|
461
|
+
...mockUser,
|
|
462
|
+
user_metadata: { globalRole: 'super_admin' }
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
const service = new RBACService(mockSupabase as any, superAdminUser, mockSession, 'test-app');
|
|
466
|
+
await service.initialize();
|
|
467
|
+
|
|
468
|
+
expect(service.hasRole('super_admin')).toBe(true);
|
|
469
|
+
expect(service.hasRole('super')).toBe(true);
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
describe('Error Handling', () => {
|
|
474
|
+
it('should handle missing dependencies gracefully', async () => {
|
|
475
|
+
const serviceWithoutUser = new RBACService(mockSupabase as any, null, null, 'test-app', true);
|
|
476
|
+
|
|
477
|
+
await serviceWithoutUser.refreshPermissions();
|
|
478
|
+
|
|
479
|
+
expect(serviceWithoutUser.getPermissions()).toEqual({});
|
|
480
|
+
expect(serviceWithoutUser.getRoles()).toEqual([]);
|
|
481
|
+
expect(serviceWithoutUser.getAccessLevel()).toBe(AccessLevel.VIEWER);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('should handle network errors', async () => {
|
|
485
|
+
mockSupabase.from().select().eq().eq().single.mockRejectedValue(new Error('Network error'));
|
|
486
|
+
|
|
487
|
+
await rbacService.initialize();
|
|
488
|
+
|
|
489
|
+
expect(rbacService.getError()).toBeDefined();
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Base Service Class
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Services/Base
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Base service class implementing the observable pattern for React subscriptions.
|
|
8
|
+
* All services extend this class to provide state change notifications.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type StateChangeCallback = () => void;
|
|
12
|
+
|
|
13
|
+
export abstract class BaseService {
|
|
14
|
+
private subscribers: Array<StateChangeCallback> = [];
|
|
15
|
+
private isInitialized = false;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Subscribe to state changes
|
|
19
|
+
* @param callback Function to call when state changes
|
|
20
|
+
* @returns Unsubscribe function
|
|
21
|
+
*/
|
|
22
|
+
subscribe(callback: StateChangeCallback): () => void {
|
|
23
|
+
this.subscribers.push(callback);
|
|
24
|
+
|
|
25
|
+
// Return unsubscribe function that removes only the first occurrence
|
|
26
|
+
return () => {
|
|
27
|
+
const index = this.subscribers.indexOf(callback);
|
|
28
|
+
if (index > -1) {
|
|
29
|
+
this.subscribers.splice(index, 1);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Notify all subscribers of state changes
|
|
36
|
+
* This triggers React re-renders
|
|
37
|
+
*/
|
|
38
|
+
protected notify(): void {
|
|
39
|
+
this.subscribers.forEach(callback => {
|
|
40
|
+
try {
|
|
41
|
+
callback();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error('[BaseService] Error in subscriber callback:', error);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Initialize the service
|
|
50
|
+
* Override in subclasses to implement initialization logic
|
|
51
|
+
*/
|
|
52
|
+
async initialize(): Promise<void> {
|
|
53
|
+
if (this.isInitialized) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await this.doInitialize();
|
|
58
|
+
this.isInitialized = true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Cleanup the service
|
|
63
|
+
* Override in subclasses to implement cleanup logic
|
|
64
|
+
*/
|
|
65
|
+
cleanup(): void {
|
|
66
|
+
this.subscribers = [];
|
|
67
|
+
this.doCleanup();
|
|
68
|
+
this.isInitialized = false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if service is initialized
|
|
73
|
+
*/
|
|
74
|
+
protected getInitialized(): boolean {
|
|
75
|
+
return this.isInitialized;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Override in subclasses to implement initialization logic
|
|
80
|
+
*/
|
|
81
|
+
protected abstract doInitialize(): Promise<void>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Override in subclasses to implement cleanup logic
|
|
85
|
+
*/
|
|
86
|
+
protected abstract doCleanup(): void;
|
|
87
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Authentication Service Interface
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Services/Interfaces
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Interface for authentication service operations.
|
|
8
|
+
* Defines the contract for authentication-related functionality.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { type SupabaseClient, type User, type Session, AuthError } from '@supabase/supabase-js';
|
|
12
|
+
|
|
13
|
+
export interface AuthResult {
|
|
14
|
+
user: User | null;
|
|
15
|
+
session: Session | null;
|
|
16
|
+
error: AuthError | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IAuthService {
|
|
20
|
+
// Auth state
|
|
21
|
+
getUser(): User | null;
|
|
22
|
+
getSession(): Session | null;
|
|
23
|
+
isAuthenticated(): boolean;
|
|
24
|
+
isLoading(): boolean;
|
|
25
|
+
getError(): AuthError | null;
|
|
26
|
+
getSupabaseClient(): SupabaseClient | null;
|
|
27
|
+
|
|
28
|
+
// Auth methods
|
|
29
|
+
signIn(email: string, password?: string): Promise<AuthResult>;
|
|
30
|
+
signUp(email: string, password: string): Promise<AuthResult>;
|
|
31
|
+
signOut(): Promise<AuthResult>;
|
|
32
|
+
resetPassword(email: string): Promise<AuthResult>;
|
|
33
|
+
updatePassword(password: string): Promise<AuthResult>;
|
|
34
|
+
refreshSession(): Promise<AuthResult>;
|
|
35
|
+
|
|
36
|
+
// Lifecycle
|
|
37
|
+
initialize(): Promise<void>;
|
|
38
|
+
cleanup(): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Event Service Interface
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Services/Interfaces
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Interface for event service operations.
|
|
8
|
+
* Defines the contract for event management functionality.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Event } from '../../types/unified';
|
|
12
|
+
|
|
13
|
+
export interface IEventService {
|
|
14
|
+
// Event state
|
|
15
|
+
getEvents(): Event[];
|
|
16
|
+
getSelectedEvent(): Event | null;
|
|
17
|
+
isLoading(): boolean;
|
|
18
|
+
getError(): Error | null;
|
|
19
|
+
|
|
20
|
+
// Event methods
|
|
21
|
+
setSelectedEvent(event: Event | null): void;
|
|
22
|
+
refreshEvents(): Promise<void>;
|
|
23
|
+
loadPersistedEvent(events: Event[]): Promise<boolean>;
|
|
24
|
+
persistEventSelection(eventId: string): void;
|
|
25
|
+
autoSelectNextEvent(events: Event[]): void;
|
|
26
|
+
|
|
27
|
+
// Lifecycle
|
|
28
|
+
initialize(): Promise<void>;
|
|
29
|
+
cleanup(): void;
|
|
30
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Inactivity Service Interface
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Services/Interfaces
|
|
5
|
+
* @since 0.1.0
|
|
6
|
+
*
|
|
7
|
+
* Interface for inactivity service operations.
|
|
8
|
+
* Defines the contract for inactivity tracking functionality.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface IInactivityService {
|
|
12
|
+
// Inactivity state
|
|
13
|
+
isIdle(): boolean;
|
|
14
|
+
getTimeRemaining(): number;
|
|
15
|
+
isWarningShown(): boolean;
|
|
16
|
+
isTracking(): boolean;
|
|
17
|
+
getShowInactivityWarning(): boolean;
|
|
18
|
+
getInactivityTimeRemaining(): number;
|
|
19
|
+
|
|
20
|
+
// Inactivity methods
|
|
21
|
+
resetActivity(): void;
|
|
22
|
+
startTracking(): void;
|
|
23
|
+
stopTracking(): void;
|
|
24
|
+
handleIdleLogout(): Promise<void>;
|
|
25
|
+
handleStaySignedIn(): void;
|
|
26
|
+
handleSignOutNow(): Promise<void>;
|
|
27
|
+
|
|
28
|
+
// Lifecycle
|
|
29
|
+
initialize(): Promise<void>;
|
|
30
|
+
cleanup(): void;
|
|
31
|
+
}
|