@jmruthers/pace-core 0.5.191 → 0.6.1
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/CHANGELOG.md +29 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +372 -0
- package/cursor-rules/01-standards-compliance.mdc +275 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +341 -0
- package/cursor-rules/04-testing-standards.mdc +315 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +392 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
- package/cursor-rules/CHANGELOG.md +101 -0
- package/cursor-rules/README.md +191 -0
- package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
- package/dist/{DataTable-WKRZD47S.js → DataTable-DQ7RSOHE.js} +8 -7
- package/dist/{PublicPageProvider-ULXC_u6U.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +37 -156
- package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
- package/dist/{UnifiedAuthProvider-FTSG5XH7.js → UnifiedAuthProvider-ATAP5UTR.js} +3 -3
- package/dist/{api-IHKALJZD.js → api-N774RPUA.js} +2 -2
- package/dist/{chunk-6C4YBBJM.js → chunk-3QRJFVBR.js} +1 -1
- package/dist/chunk-3QRJFVBR.js.map +1 -0
- package/dist/{chunk-OETXORNB.js → chunk-3XTALGJF.js} +211 -136
- package/dist/chunk-3XTALGJF.js.map +1 -0
- package/dist/{chunk-6TQDD426.js → chunk-4N5C5XZU.js} +47 -228
- package/dist/chunk-4N5C5XZU.js.map +1 -0
- package/dist/{chunk-LOMZXPSN.js → chunk-4ZC4GX36.js} +47 -74
- package/dist/chunk-4ZC4GX36.js.map +1 -0
- package/dist/{chunk-6LTQQAT6.js → chunk-BYFSK72L.js} +357 -158
- package/dist/chunk-BYFSK72L.js.map +1 -0
- package/dist/{chunk-XYXSXPUK.js → chunk-EXUD6RNJ.js} +50 -10
- package/dist/chunk-EXUD6RNJ.js.map +1 -0
- package/dist/{chunk-VKB2CO4Z.js → chunk-GLK6VM3F.js} +244 -249
- package/dist/chunk-GLK6VM3F.js.map +1 -0
- package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-XNYQOL3Z.js → chunk-JBKQ3SAO.js} +9 -18
- package/dist/chunk-JBKQ3SAO.js.map +1 -0
- package/dist/{chunk-ROXMHMY2.js → chunk-KNC55RTG.js} +13 -3
- package/dist/{chunk-ROXMHMY2.js.map → chunk-KNC55RTG.js.map} +1 -1
- package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
- package/dist/{chunk-ULHIJK66.js → chunk-T33XF5ZC.js} +255 -140
- package/dist/chunk-T33XF5ZC.js.map +1 -0
- package/dist/{chunk-VRGWKHDB.js → chunk-XM25TVIE.js} +100 -33
- package/dist/chunk-XM25TVIE.js.map +1 -0
- package/dist/components.d.ts +4 -4
- package/dist/components.js +9 -9
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +20 -25
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +18 -21
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +2 -20
- package/dist/rbac/index.js +7 -9
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -3
- 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/Logger.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/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +10 -10
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.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/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.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/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +24 -11
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.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/LoggerConfig.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 +2 -2
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.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/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +2 -2
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.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/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.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 +60 -38
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.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 +2 -2
- 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/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.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 +194 -209
- package/docs/getting-started/cursor-rules.md +262 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/MIGRATION_GUIDE.md +4 -4
- package/docs/migration/REACT_19_MIGRATION.md +227 -0
- package/docs/migration/database-changes-december-2025.md +2 -1
- package/docs/rbac/event-based-apps.md +124 -6
- package/docs/standards/README.md +39 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +11 -6
- package/scripts/audit-consuming-app.cjs +961 -0
- package/scripts/check-pace-core-compliance.cjs +315 -61
- package/scripts/install-cursor-rules.cjs +236 -0
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/__tests__/rls-policies.test.ts +3 -1
- package/src/components/Badge/Badge.tsx +2 -4
- package/src/components/Button/Button.tsx +5 -4
- package/src/components/Calendar/Calendar.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
- package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +88 -16
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/DataTableCore.tsx +4 -7
- package/src/components/DataTable/components/DataTableModals.tsx +1 -1
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +86 -17
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +6 -5
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -3
- package/src/components/FileDisplay/FileDisplay.tsx +16 -4
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +30 -26
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.tsx +28 -30
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +8 -8
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -11
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -1
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +75 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +98 -69
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -8
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
- package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
- package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
- package/src/components/Select/Select.tsx +33 -22
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/UserMenu/UserMenu.tsx +1 -1
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +14 -7
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/public/usePublicEvent.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/services/useAuthService.ts +21 -3
- package/src/hooks/services/useEventService.ts +21 -3
- package/src/hooks/services/useInactivityService.ts +21 -3
- package/src/hooks/services/useOrganisationService.ts +21 -3
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useFileDisplay.ts +10 -17
- package/src/hooks/useFocusManagement.ts +2 -2
- package/src/hooks/useFocusTrap.ts +4 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +1 -1
- package/src/hooks/usePermissionCache.ts +1 -1
- package/src/hooks/useSecureDataAccess.test.ts +16 -9
- package/src/hooks/useSecureDataAccess.ts +22 -6
- package/src/hooks/useToast.ts +2 -2
- package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/EventServiceProvider.tsx +0 -8
- package/src/providers/services/UnifiedAuthProvider.tsx +196 -46
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +34 -40
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
- package/src/rbac/adapters.tsx +3 -22
- package/src/rbac/api.test.ts +2 -2
- package/src/rbac/api.ts +7 -1
- package/src/rbac/components/EnhancedNavigationMenu.tsx +3 -16
- package/src/rbac/components/NavigationGuard.tsx +2 -11
- package/src/rbac/components/NavigationProvider.tsx +1 -2
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +46 -13
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.tsx +1 -2
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
- package/src/rbac/engine.ts +14 -2
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/usePermissions.ts +51 -11
- package/src/rbac/hooks/useRBAC.ts +3 -13
- package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
- package/src/rbac/hooks/useResolvedScope.ts +58 -33
- package/src/rbac/hooks/useSecureSupabase.ts +4 -9
- package/src/rbac/secureClient.ts +43 -0
- package/src/services/EventService.ts +4 -57
- package/src/services/InactivityService.ts +127 -34
- package/src/services/OrganisationService.ts +68 -10
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-6LTQQAT6.js.map +0 -1
- package/dist/chunk-6TQDD426.js.map +0 -1
- package/dist/chunk-LOMZXPSN.js.map +0 -1
- package/dist/chunk-OETXORNB.js.map +0 -1
- package/dist/chunk-ULHIJK66.js.map +0 -1
- package/dist/chunk-VKB2CO4Z.js.map +0 -1
- package/dist/chunk-VRGWKHDB.js.map +0 -1
- package/dist/chunk-XNYQOL3Z.js.map +0 -1
- package/dist/chunk-XYXSXPUK.js.map +0 -1
- package/scripts/check-pace-core-compliance.js +0 -512
- package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
- package/src/utils/context/superAdminOverride.ts +0 -58
- /package/dist/{DataTable-WKRZD47S.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-FTSG5XH7.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
- /package/dist/{api-IHKALJZD.js.map → api-N774RPUA.js.map} +0 -0
- /package/dist/{chunk-QWWZ5CAQ.js.map → chunk-LXQLPRQ2.js.map} +0 -0
- /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/services/UnifiedAuthProvider.tsx","../src/providers/services/AuthServiceProvider.tsx","../src/services/AuthService.ts","../src/services/base/BaseService.ts","../src/providers/services/OrganisationServiceProvider.tsx","../src/services/OrganisationService.ts","../src/providers/services/EventServiceProvider.tsx","../src/services/EventService.ts","../src/providers/services/InactivityServiceProvider.tsx","../src/services/InactivityService.ts","../src/hooks/services/useAuthService.ts","../src/hooks/services/useOrganisationService.ts","../src/hooks/useOrganisations.ts","../src/hooks/services/useEventService.ts","../src/hooks/services/useInactivityService.ts","../src/hooks/useSessionRestoration.ts"],"sourcesContent":["/**\n * @file Unified Auth Provider\n * @package @jmruthers/pace-core\n * @module Providers/Services\n * @since 0.1.0\n *\n * Unified authentication provider for authentication, organisations, events, and inactivity tracking.\n * Note: RBAC functionality is available via useRBAC() hook from '@jmruthers/pace-core/rbac'\n */\n\nimport React, { createContext, useContext, useMemo, useCallback, useRef, useEffect, useState, useReducer } from 'react';\nimport { type SupabaseClient, type User, type Session } from '@supabase/supabase-js';\nimport { AuthServiceProvider } from './AuthServiceProvider';\nimport { OrganisationServiceProvider } from './OrganisationServiceProvider';\nimport { EventServiceProvider } from './EventServiceProvider';\nimport { InactivityServiceProvider } from './InactivityServiceProvider';\nimport { useAuthService } from '../../hooks/services/useAuthService';\nimport { useOrganisationService } from '../../hooks/services/useOrganisationService';\nimport { useOrganisations } from '../../hooks/useOrganisations';\nimport { useEventService } from '../../hooks/services/useEventService';\nimport { useInactivityService } from '../../hooks/services/useInactivityService';\nimport { useSessionRestoration } from '../../hooks/useSessionRestoration';\nimport type { Organisation, OrganisationMembership } from '../../types/organisation';\nimport type { Event } from '../../types/event';\nimport type { AuthError } from '@supabase/supabase-js';\nimport type { SessionRestorationState } from '../../types/auth';\nimport { logger } from '../../utils/core/logger';\n\n// Re-export UserEventAccess type\nexport interface UserEventAccess {\n event_id: string;\n event_name: string;\n event_description?: string | null;\n start_date: string;\n end_date: string;\n event_status: string;\n app_id: string;\n access_level: string;\n granted_at: string;\n organisation_id: string;\n}\n\n// Combined context type - focuses on auth, organisations, events, and inactivity\nexport interface UnifiedAuthContextType {\n // Auth state\n user: User | null;\n session: Session | null;\n isAuthenticated: boolean;\n authLoading: boolean;\n authError: AuthError | null;\n supabase: SupabaseClient | null;\n \n // App context (resolved immediately on login)\n appName: string;\n appId: string | undefined; // Resolved from appName when user logs in\n \n // Auth methods\n signIn: (email: string, password?: string) => Promise<{ error: AuthError | null }>;\n signUp: (email: string, password: string) => Promise<{ error: AuthError | null }>;\n signOut: () => Promise<{ error: AuthError | null }>;\n resetPassword: (email: string) => Promise<{ error: AuthError | null }>;\n updatePassword: (password: string) => Promise<{ error: AuthError | null }>;\n refreshSession: () => Promise<{ error: AuthError | null }>;\n\n // Organisation state\n selectedOrganisation: Organisation | null;\n selectedOrganisationId: string | null;\n organisations: Organisation[];\n userMemberships: OrganisationMembership[];\n organisationLoading: boolean;\n organisationError: Error | null;\n hasValidOrganisationContext: boolean;\n isContextReady: boolean;\n \n // Organisation methods\n switchOrganisation: (orgId: string) => Promise<void>;\n getUserRole: (orgId?: string) => string;\n validateOrganisationAccess: (orgId: string) => boolean;\n refreshOrganisations: () => Promise<void>;\n ensureOrganisationContext: () => Organisation;\n isOrganisationSecure: () => boolean;\n getPrimaryOrganisation: () => Organisation | null;\n\n // Event state\n events: Event[];\n selectedEvent: Event | null;\n selectedEventId: string | null;\n eventLoading: boolean;\n eventError: Error | null;\n \n // Event methods\n setSelectedEvent: (event: Event | null) => void;\n refreshEvents: () => Promise<void>;\n\n // Inactivity state\n showInactivityWarning: boolean;\n inactivityTimeRemaining: number;\n isIdle: boolean;\n timeRemaining: number;\n showWarning: boolean;\n isTracking: boolean;\n \n // Inactivity methods\n resetActivity: () => void;\n startTracking: () => void;\n stopTracking: () => void;\n handleIdleLogout: () => Promise<void>;\n handleStaySignedIn: () => void;\n handleSignOutNow: () => Promise<void>;\n\n // Additional unified properties\n appConfig: { requires_event: boolean } | null;\n isLoading: boolean;\n hasErrors: boolean;\n sessionRestoration: SessionRestorationState;\n sessionRestorationTimedOut: boolean;\n sessionRestorationTimeoutMs: number;\n}\n\nexport const UnifiedAuthContext = createContext<UnifiedAuthContextType | undefined>(undefined);\n\nexport const useUnifiedAuth = () => {\n const context = useContext(UnifiedAuthContext);\n if (!context) {\n // Provide a helpful error log in addition to throwing for testability and DX\n logger.error('useUnifiedAuth', 'useUnifiedAuth must be used within a UnifiedAuthProvider');\n throw new Error('useUnifiedAuth must be used within a UnifiedAuthProvider');\n }\n return context;\n};\n\nexport interface UnifiedAuthProviderProps {\n children: React.ReactNode;\n supabaseClient: SupabaseClient;\n appName: string;\n persistState?: boolean;\n enablePersistence?: boolean;\n requireOrganisationContext?: boolean;\n \n // App configuration\n appConfig?: { requires_event: boolean } | null;\n \n // Inactivity auto-logout configuration - MANDATORY for security\n idleTimeoutMs: number; // REQUIRED: Inactivity timeout in milliseconds\n warnBeforeMs: number; // REQUIRED: Warning time before logout in milliseconds\n onIdleLogout: (reason: 'inactivity') => void; // REQUIRED: App handles redirect/navigation\n renderInactivityWarning?: (args: {\n timeRemaining: number;\n onStaySignedIn: () => void;\n onSignOutNow: () => void;\n }) => React.ReactNode; // Optional custom warning UI\n dangerouslyDisableInactivity?: boolean; // Dev-only; must not disable in production\n}\n\n// Internal component that combines all contexts\nfunction UnifiedAuthContextProvider({ \n children, \n appName,\n appConfig: appConfigProp,\n supabaseClient: supabaseClientProp,\n ...props \n}: UnifiedAuthProviderProps) {\n const authService = useAuthService();\n const organisationService = useOrganisationService();\n const inactivityService = useInactivityService();\n const sessionRestorationState = useSessionRestoration();\n const {\n hasTimedOut: sessionRestorationTimedOut,\n timeoutMs: sessionRestorationTimeoutMs,\n isRestoring,\n restorationComplete,\n restorationError,\n } = sessionRestorationState;\n const sessionRestoration: SessionRestorationState = useMemo(() => ({\n isRestoring,\n restorationComplete,\n restorationError,\n }), [isRestoring, restorationComplete, restorationError]);\n \n // Load appConfig from database if not provided as prop\n // Memoize appConfig to prevent object reference changes that cause re-renders\n const [appConfigState, setAppConfigState] = useState<{ requires_event: boolean } | null>(appConfigProp || null);\n const isResolvingAppConfigRef = useRef(false);\n const resolvedAppConfigRef = useRef<{ requires_event: boolean } | null>(null);\n \n // Memoize appConfig to ensure stable reference - only recreate if requires_event changes\n const appConfig = useMemo(() => {\n if (!appConfigState) return null;\n return { requires_event: appConfigState.requires_event };\n }, [appConfigState?.requires_event]);\n\n // Try to get event service, but provide fallback if not available\n let eventService;\n try {\n eventService = useEventService();\n } catch (error) {\n // EventService not available - provide fallback implementation\n // Include subscribe method to match BaseService interface\n eventService = {\n getEvents: () => [],\n getSelectedEvent: () => null,\n isLoading: () => false,\n getError: () => null,\n setSelectedEvent: () => {},\n refreshEvents: async () => {},\n subscribe: () => () => {} // No-op subscribe/unsubscribe for fallback\n };\n }\n\n // Get current auth state - these will trigger re-renders when services change\n const currentUser = authService.getUser();\n const currentSession = authService.getSession();\n const isAuth = !!(currentUser && currentSession);\n \n // Stabilize supabase client reference to prevent infinite re-renders\n // Memoize the supabaseClient prop to ensure a stable reference across renders\n // This is critical for preventing infinite loops when supabase is used in useEffect dependency arrays\n // The reference will only change when supabaseClientProp actually changes (different instance)\n // If the consuming app creates the client once and reuses it, this will be stable\n const supabase = useMemo(() => supabaseClientProp, [supabaseClientProp]);\n \n // Resolve appId immediately when user logs in (don't wait for organisation/event)\n // This makes appId available early for navigation menu filtering\n const [appId, setAppId] = useState<string | undefined>(undefined);\n const isResolvingAppIdRef = useRef(false);\n const resolvedAppIdRef = useRef<string | undefined>(undefined);\n const resolvedUserIdRef = useRef<string | undefined>(undefined);\n \n useEffect(() => {\n // Clear appId when user logs out or changes\n if (!isAuth) {\n // User logged out - clear all refs and state\n resolvedAppIdRef.current = undefined;\n resolvedUserIdRef.current = undefined;\n setAppId(undefined);\n return;\n }\n \n // User changed - clear previous resolution to allow re-resolution for new user\n if (currentUser?.id && resolvedUserIdRef.current && resolvedUserIdRef.current !== currentUser.id) {\n resolvedAppIdRef.current = undefined;\n resolvedUserIdRef.current = undefined;\n setAppId(undefined);\n }\n \n // Resolve appId as soon as we have user + supabase + appName\n // Don't wait for organisation or event - appId resolution only needs user + appName\n // Extract userId to stable const to prevent reference issues\n const currentUserId = currentUser?.id;\n \n // Only resolve if we haven't already resolved for this user\n if (\n isAuth &&\n currentUserId &&\n supabase &&\n appName &&\n resolvedUserIdRef.current !== currentUserId && // Haven't resolved for this user yet\n !isResolvingAppIdRef.current\n ) {\n // CRITICAL FIX: Set refs IMMEDIATELY (synchronously) before async operation\n // This prevents race conditions where effect re-runs before async completes\n isResolvingAppIdRef.current = true;\n resolvedUserIdRef.current = currentUserId; // Set BEFORE async, not after\n \n const userId = currentUserId;\n const appNameValue = appName;\n \n // Resolve appId immediately\n import('../../rbac/api').then(async ({ resolveAppContext, setupRBAC }) => {\n try {\n // Ensure RBAC is initialized (idempotent - safe to call multiple times)\n setupRBAC(supabase);\n \n const result = await resolveAppContext({\n userId,\n appName: appNameValue\n });\n \n if (result?.appId) {\n resolvedAppIdRef.current = result.appId;\n // resolvedUserIdRef already set above to prevent race conditions\n setAppId(result.appId);\n } else {\n // No appId returned - reset ref to allow retry\n resolvedUserIdRef.current = undefined;\n }\n } catch (error) {\n logger.error('UnifiedAuthProvider', 'Failed to resolve appId on login', {\n error: error instanceof Error ? error.message : String(error),\n appName: appNameValue,\n userId\n });\n // Reset ref on error to allow retry\n resolvedUserIdRef.current = undefined;\n } finally {\n isResolvingAppIdRef.current = false;\n }\n }).catch((importError) => {\n logger.error('UnifiedAuthProvider', 'Failed to import RBAC API', importError);\n isResolvingAppIdRef.current = false;\n // Reset ref on import error to allow retry\n resolvedUserIdRef.current = undefined;\n });\n }\n }, [isAuth, currentUser?.id, supabase, appName]); // Removed appId from deps - it's the output, not an input. currentUser?.id is stable primitive.\n\n // Load appConfig from database if not provided as prop\n useEffect(() => {\n // If appConfig is provided as prop, use it and don't load from database\n if (appConfigProp !== undefined) {\n setAppConfigState(appConfigProp);\n resolvedAppConfigRef.current = appConfigProp;\n return;\n }\n\n // If we've already resolved, don't resolve again\n if (resolvedAppConfigRef.current !== null || isResolvingAppConfigRef.current) {\n return;\n }\n\n // Only load if we have supabase and appName\n if (!supabase || !appName) {\n return;\n }\n\n isResolvingAppConfigRef.current = true;\n\n // Load app config from database\n import('../../rbac/api').then(async ({ getAppConfigByName }) => {\n try {\n const config = await getAppConfigByName(appName);\n // Default to requires_event: false if config is null (organisation-based apps)\n const resolvedConfig = config || { requires_event: false };\n \n // Only update if the value actually changed to prevent unnecessary re-renders\n if (resolvedAppConfigRef.current?.requires_event !== resolvedConfig.requires_event) {\n resolvedAppConfigRef.current = resolvedConfig;\n setAppConfigState(resolvedConfig);\n }\n \n // Debug logging for pace-mint\n if (import.meta.env.DEV && appName === 'MINT') {\n logger.debug('UnifiedAuthProvider', 'App config loaded', {\n appName,\n config: resolvedConfig,\n requiresEvent: resolvedConfig.requires_event\n });\n }\n } catch (error) {\n logger.warn('UnifiedAuthProvider', 'Failed to load app config, defaulting to organisation-based', {\n error: error instanceof Error ? error.message : String(error),\n appName\n });\n // Default to organisation-based (requires_event: false) on error\n // Only update if not already set to avoid unnecessary re-renders\n if (resolvedAppConfigRef.current?.requires_event !== false) {\n const defaultConfig = { requires_event: false };\n resolvedAppConfigRef.current = defaultConfig;\n setAppConfigState(defaultConfig);\n }\n } finally {\n isResolvingAppConfigRef.current = false;\n }\n }).catch((importError) => {\n logger.error('UnifiedAuthProvider', 'Failed to import RBAC API for app config', importError);\n isResolvingAppConfigRef.current = false;\n // Default to organisation-based on import error\n // Only update if not already set to avoid unnecessary re-renders\n if (resolvedAppConfigRef.current?.requires_event !== false) {\n const defaultConfig = { requires_event: false };\n resolvedAppConfigRef.current = defaultConfig;\n setAppConfigState(defaultConfig);\n }\n });\n }, [supabase, appName, appConfigProp]);\n\n // Subscribe to service state changes to trigger re-renders\n // Use useReducer to force updates when services notify\n const [, forceUpdate] = useReducer(x => x + 1, 0);\n const forceUpdateTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const debouncedForceUpdate = useCallback(() => {\n if (forceUpdateTimeoutRef.current) {\n clearTimeout(forceUpdateTimeoutRef.current);\n }\n\n forceUpdateTimeoutRef.current = setTimeout(() => {\n forceUpdate();\n forceUpdateTimeoutRef.current = null;\n }, 100); // Batch updates - 100ms debounce to prevent excessive re-renders\n // Reduced from 16ms to 100ms to better batch service state updates\n // and prevent flickering when multiple services update in quick succession\n }, [forceUpdate]);\n\n // Use refs for services to avoid dependency on service instances\n // This prevents re-subscriptions when service instances change reference\n const authServiceRef = useRef(authService);\n const organisationServiceRef = useRef(organisationService);\n const eventServiceRef = useRef(eventService);\n const inactivityServiceRef = useRef(inactivityService);\n\n // Update refs when services change\n useEffect(() => {\n authServiceRef.current = authService;\n organisationServiceRef.current = organisationService;\n eventServiceRef.current = eventService;\n inactivityServiceRef.current = inactivityService;\n }, [authService, organisationService, eventService, inactivityService]);\n\n // Subscribe using refs - only depends on debouncedForceUpdate\n // This prevents re-subscriptions when service instances change\n useEffect(() => {\n // Subscribe to all service changes using current refs\n const unsubscribeAuth = authServiceRef.current.subscribe(debouncedForceUpdate);\n const unsubscribeOrg = organisationServiceRef.current.subscribe(debouncedForceUpdate);\n const unsubscribeEvent = eventServiceRef.current.subscribe(debouncedForceUpdate);\n const unsubscribeInactivity = inactivityServiceRef.current.subscribe(debouncedForceUpdate);\n\n return () => {\n unsubscribeAuth();\n unsubscribeOrg();\n unsubscribeEvent();\n unsubscribeInactivity();\n\n if (forceUpdateTimeoutRef.current) {\n clearTimeout(forceUpdateTimeoutRef.current);\n }\n };\n }, [debouncedForceUpdate]);\n\n // Get loading states - these will trigger re-renders when services change\n const authLoading = authService.isLoading();\n const orgLoading = organisationService.isLoading();\n const eventLoading = eventService.isLoading();\n const restorationLoading = sessionRestoration.isRestoring && !sessionRestorationTimedOut && !sessionRestoration.restorationError;\n // For ADMIN/PORTAL apps, don't block on organisation loading (super admins can proceed)\n const shouldIncludeOrgLoading = appName !== 'ADMIN' && appName !== 'PORTAL';\n const totalLoading = restorationLoading || authLoading || (shouldIncludeOrgLoading ? orgLoading : false) || eventLoading;\n \n // Extract all primitive values from services to use in dependencies\n const authError = authService.getError();\n // supabase is already declared above (line 198)\n const rawSelectedOrganisation = organisationService.getSelectedOrganisation();\n const organisationError = organisationService.getError();\n \n // For event-required apps, selectedOrganisation is not in context (org derived from event)\n // For org-required apps, selectedOrganisation is available\n // CRITICAL FIX: Only set to null if appConfig is loaded AND explicitly requires_event is true\n // If appConfig is null (still loading) OR requires_event is false/undefined, allow selectedOrganisation\n // This ensures organisation-based apps work correctly even if appConfig hasn't loaded yet or is misconfigured\n // IMPORTANT: If rawSelectedOrganisation exists, prefer it over appConfig to avoid race conditions\n const selectedOrganisation = (appConfig !== null && appConfig?.requires_event === true && !rawSelectedOrganisation) \n ? null \n : rawSelectedOrganisation;\n \n // Debug logging for pace-mint issue - use useEffect to avoid causing re-renders\n useEffect(() => {\n if (import.meta.env.DEV && appName === 'MINT') {\n logger.debug('UnifiedAuthProvider', 'Organisation state check', {\n rawSelectedOrganisation: rawSelectedOrganisation?.id || null,\n rawSelectedOrganisationType: typeof rawSelectedOrganisation,\n appConfig,\n appConfigRequiresEvent: appConfig?.requires_event,\n selectedOrganisation: selectedOrganisation?.id || null,\n selectedOrganisationId: selectedOrganisation?.id || null,\n checkResult: appConfig?.requires_event === true,\n });\n }\n }, [appName, rawSelectedOrganisation?.id, appConfig?.requires_event, selectedOrganisation?.id]);\n const hasValidOrganisationContext = organisationService.hasValidOrganisationContext();\n const isContextReady = organisationService.isContextReady();\n \n // Get raw data from services\n const rawEvents = eventService.getEvents();\n const rawOrganisations = organisationService.getOrganisations();\n const rawUserMemberships = organisationService.getUserMemberships();\n \n // Memoize arrays to prevent unnecessary context updates when service returns same data\n // Compare by IDs to detect actual changes, not just reference changes\n const events = useMemo(() => {\n return rawEvents;\n }, [\n // Create dependency string from event IDs - only changes when events actually change\n rawEvents.map(e => e.event_id || e.id).join(',')\n ]);\n \n const organisations = useMemo(() => {\n return rawOrganisations;\n }, [\n // Create dependency string from organisation IDs - only changes when orgs actually change\n rawOrganisations.map(o => o.id).join(',')\n ]);\n \n const userMemberships = useMemo(() => {\n return rawUserMemberships;\n }, [\n // Create dependency string from membership IDs - only changes when memberships actually change\n rawUserMemberships.map(m => `${m.organisation_id}-${m.user_id}`).join(',')\n ]);\n \n const selectedEvent = eventService.getSelectedEvent();\n const eventError = eventService.getError();\n const showInactivityWarning = inactivityService.getShowInactivityWarning();\n const inactivityTimeRemaining = inactivityService.getInactivityTimeRemaining();\n const isIdle = inactivityService.isIdle();\n const timeRemaining = inactivityService.getTimeRemaining();\n const showWarning = inactivityService.isWarningShown();\n const isTracking = inactivityService.isTracking();\n \n // Memoize inactivity values to prevent unnecessary context updates\n const inactivityState = useMemo(() => ({\n showInactivityWarning,\n inactivityTimeRemaining,\n isIdle,\n timeRemaining,\n showWarning,\n isTracking,\n }), [\n showInactivityWarning,\n inactivityTimeRemaining,\n isIdle,\n timeRemaining,\n showWarning,\n isTracking,\n ]);\n \n const hasErrors = !!(authError || organisationError || eventError || sessionRestoration.restorationError);\n\n // React Compiler handles memoization automatically\n const signIn = (email: string, password?: string) => authService.signIn(email, password);\n const signUp = (email: string, password: string) => authService.signUp(email, password);\n const signOut = () => authService.signOut();\n const resetPassword = (email: string) => authService.resetPassword(email);\n const updatePassword = (password: string) => authService.updatePassword(password);\n const refreshSession = () => authService.refreshSession();\n \n const switchOrganisation = (orgId: string) => organisationService.switchOrganisation(orgId);\n const getUserRole = (orgId?: string) => organisationService.getUserRole(orgId);\n const validateOrganisationAccess = (orgId: string) => organisationService.validateOrganisationAccess(orgId);\n const refreshOrganisations = () => organisationService.refreshOrganisations();\n const ensureOrganisationContext = () => organisationService.ensureOrganisationContext();\n const isOrganisationSecure = () => organisationService.isOrganisationSecure();\n const getPrimaryOrganisation = () => organisationService.getPrimaryOrganisation();\n \n const setSelectedEvent = (event: Event | null) => eventService.setSelectedEvent(event);\n const refreshEvents = () => eventService.refreshEvents();\n \n const resetActivity = () => inactivityService.resetActivity();\n const startTracking = () => inactivityService.startTracking();\n const stopTracking = () => inactivityService.stopTracking();\n const handleIdleLogout = () => inactivityService.handleIdleLogout();\n const handleStaySignedIn = () => inactivityService.handleStaySignedIn();\n const handleSignOutNow = () => inactivityService.handleSignOutNow();\n\n // Use ref to track previous state for conditional logging (dev only)\n const prevStateRef = useRef<{\n isAuthenticated: boolean;\n userEmail: string | undefined;\n totalLoading: boolean;\n } | null>(null);\n \n // Only log when state actually changes (dev mode detection)\n const isDev = import.meta.env.DEV || import.meta.env.MODE === 'development';\n if (isDev) {\n const currentState = {\n isAuthenticated: isAuth,\n userEmail: currentUser?.email,\n totalLoading,\n };\n \n const prevState = prevStateRef.current;\n if (!prevState || \n prevState.isAuthenticated !== currentState.isAuthenticated ||\n prevState.userEmail !== currentState.userEmail ||\n prevState.totalLoading !== currentState.totalLoading) {\n // State change detected, notify subscribers\n prevStateRef.current = currentState;\n }\n }\n \n // Memoized combined context value - only depends on primitive values\n const contextValue = useMemo<UnifiedAuthContextType>(() => {\n return {\n // Auth state\n user: currentUser,\n session: currentSession,\n isAuthenticated: isAuth,\n authLoading: authLoading,\n authError: authError,\n supabase: supabase,\n \n // Auth methods\n signIn,\n signUp,\n signOut,\n resetPassword,\n updatePassword,\n refreshSession,\n\n // Organisation state\n selectedOrganisation: selectedOrganisation,\n selectedOrganisationId: selectedOrganisation?.id || null,\n organisations: organisations,\n userMemberships: userMemberships,\n organisationLoading: orgLoading,\n organisationError: organisationError,\n hasValidOrganisationContext: hasValidOrganisationContext,\n isContextReady: isContextReady,\n \n // Organisation methods\n switchOrganisation,\n getUserRole,\n validateOrganisationAccess,\n refreshOrganisations,\n ensureOrganisationContext,\n isOrganisationSecure,\n getPrimaryOrganisation,\n\n // Event state\n events: events,\n selectedEvent: selectedEvent,\n selectedEventId: selectedEvent?.event_id || null,\n eventLoading: eventLoading,\n eventError: eventError,\n \n // Event methods\n setSelectedEvent,\n refreshEvents,\n\n // Inactivity state\n showInactivityWarning: inactivityState.showInactivityWarning,\n inactivityTimeRemaining: inactivityState.inactivityTimeRemaining,\n isIdle: inactivityState.isIdle,\n timeRemaining: inactivityState.timeRemaining,\n showWarning: inactivityState.showWarning,\n isTracking: inactivityState.isTracking,\n \n // Inactivity methods\n resetActivity,\n startTracking,\n stopTracking,\n handleIdleLogout,\n handleStaySignedIn,\n handleSignOutNow,\n\n // Additional unified properties\n appName,\n appId, // Resolved immediately on login\n appConfig: appConfig,\n isLoading: totalLoading,\n hasErrors: hasErrors,\n sessionRestoration: sessionRestoration,\n sessionRestorationTimedOut,\n sessionRestorationTimeoutMs,\n };\n }, [\n // All primitive values extracted from services\n // Note: Arrays/objects from services are stable references (same reference unless data changes)\n currentUser,\n currentSession,\n isAuth,\n authLoading,\n authError,\n supabase,\n selectedOrganisation,\n organisations,\n userMemberships,\n orgLoading,\n organisationError,\n hasValidOrganisationContext,\n isContextReady,\n events,\n selectedEvent,\n eventLoading,\n eventError,\n inactivityState, // Use memoized object instead of individual values\n totalLoading,\n hasErrors,\n appName,\n appId,\n appConfig,\n sessionRestoration,\n sessionRestorationTimedOut,\n sessionRestorationTimeoutMs,\n // Stable function references from useCallback (services are stable, so callbacks are too)\n signIn,\n signUp,\n signOut,\n resetPassword,\n updatePassword,\n refreshSession,\n switchOrganisation,\n getUserRole,\n validateOrganisationAccess,\n refreshOrganisations,\n ensureOrganisationContext,\n isOrganisationSecure,\n getPrimaryOrganisation,\n setSelectedEvent,\n refreshEvents,\n resetActivity,\n startTracking,\n stopTracking,\n handleIdleLogout,\n handleStaySignedIn,\n handleSignOutNow,\n ]);\n\n return (\n <UnifiedAuthContext.Provider value={contextValue}>\n {children}\n </UnifiedAuthContext.Provider>\n );\n}\n\n// Wrapper for EventServiceProvider that reads selectedOrganisation from OrganisationService\nfunction EventServiceProviderWrapper({ \n children, \n supabaseClient,\n user,\n session,\n appName,\n appConfig\n}: {\n children: React.ReactNode;\n supabaseClient: SupabaseClient;\n user: User | null;\n session: Session | null;\n appName: string;\n appConfig?: { requires_event: boolean } | null;\n}) {\n // Use useOrganisations() hook for better reactivity - it subscribes to service changes\n // This ensures EventServiceProvider re-renders when selectedOrganisation changes\n const { selectedOrganisation: rawSelectedOrganisation } = useOrganisations();\n\n // FIX: For event-required apps, don't pass selectedOrganisation to EventService\n // Organisation will be derived from the selected event instead\n // This prevents EventService from filtering events by the wrong organisation\n // CRITICAL FIX: Only set to null if appConfig is loaded AND explicitly requires_event is true AND no org selected\n // If rawSelectedOrganisation exists, prefer it over appConfig to avoid race conditions\n const selectedOrganisation = (appConfig !== null && appConfig?.requires_event === true && !rawSelectedOrganisation) \n ? null \n : rawSelectedOrganisation;\n\n // Always render EventServiceProvider - it handles null user/session gracefully\n // This ensures EventServiceContext is always available for components calling useEvents()\n // Memoize setSelectedEventId to prevent unnecessary re-initializations\n const setSelectedEventId = useCallback(() => {\n // Event selection is now handled at the application level\n }, []);\n\n return (\n <EventServiceProvider\n supabaseClient={supabaseClient}\n user={user}\n session={session}\n appName={appName}\n selectedOrganisation={selectedOrganisation}\n setSelectedEventId={setSelectedEventId}\n >\n {children}\n </EventServiceProvider>\n );\n}\n\n// Internal component that provides user/session to child providers\nfunction ServiceAwareProviders({ \n children, \n supabaseClient,\n appName,\n appConfig,\n persistState,\n enablePersistence,\n requireOrganisationContext,\n idleTimeoutMs,\n warnBeforeMs,\n onIdleLogout,\n renderInactivityWarning,\n dangerouslyDisableInactivity\n}: UnifiedAuthProviderProps) {\n const authService = useAuthService();\n\n return (\n <OrganisationServiceProvider\n supabaseClient={supabaseClient}\n user={authService.getUser()}\n session={authService.getSession()}\n >\n <EventServiceProviderWrapper\n supabaseClient={supabaseClient}\n user={authService.getUser()}\n session={authService.getSession()}\n appName={appName}\n appConfig={appConfig}\n >\n <InactivityServiceProvider\n supabaseClient={supabaseClient}\n user={authService.getUser()}\n session={authService.getSession()}\n idleTimeoutMs={idleTimeoutMs}\n warnBeforeMs={warnBeforeMs}\n onIdleLogout={onIdleLogout}\n >\n <UnifiedAuthContextProvider\n appName={appName}\n appConfig={appConfig}\n supabaseClient={supabaseClient}\n persistState={persistState}\n enablePersistence={enablePersistence}\n requireOrganisationContext={requireOrganisationContext}\n idleTimeoutMs={idleTimeoutMs}\n warnBeforeMs={warnBeforeMs}\n onIdleLogout={onIdleLogout}\n renderInactivityWarning={renderInactivityWarning}\n dangerouslyDisableInactivity={dangerouslyDisableInactivity}\n >\n {children}\n </UnifiedAuthContextProvider>\n </InactivityServiceProvider>\n </EventServiceProviderWrapper>\n </OrganisationServiceProvider>\n );\n}\n\nexport function UnifiedAuthProvider({\n children,\n supabaseClient,\n appName,\n appConfig = { requires_event: true }, // Default to requiring events\n persistState = true,\n enablePersistence,\n requireOrganisationContext = true,\n idleTimeoutMs, // REQUIRED: No default - must be explicitly provided\n warnBeforeMs, // REQUIRED: No default - must be explicitly provided\n onIdleLogout, // REQUIRED: No default - must be explicitly provided\n renderInactivityWarning,\n dangerouslyDisableInactivity = false\n}: UnifiedAuthProviderProps) {\n // Warn if supabaseClient reference changes (indicates multiple client instances)\n const clientRef = useRef(supabaseClient);\n useEffect(() => {\n if (clientRef.current !== supabaseClient) {\n logger.warn('UnifiedAuthProvider', 'Supabase client reference changed - this may indicate multiple client instances are being created', {\n previousClient: clientRef.current,\n newClient: supabaseClient,\n note: 'Ensure you create the Supabase client once and reuse it. Creating multiple clients can cause performance issues and the \"Multiple GoTrueClient instances\" warning.'\n });\n clientRef.current = supabaseClient;\n }\n }, [supabaseClient]);\n\n return (\n <AuthServiceProvider supabaseClient={supabaseClient} appName={appName}>\n <ServiceAwareProviders\n supabaseClient={supabaseClient}\n appName={appName}\n appConfig={appConfig}\n persistState={persistState}\n enablePersistence={enablePersistence}\n requireOrganisationContext={requireOrganisationContext}\n idleTimeoutMs={idleTimeoutMs}\n warnBeforeMs={warnBeforeMs}\n onIdleLogout={onIdleLogout}\n renderInactivityWarning={renderInactivityWarning}\n dangerouslyDisableInactivity={dangerouslyDisableInactivity}\n >\n {children}\n </ServiceAwareProviders>\n </AuthServiceProvider>\n );\n}\n\n","/**\n * @file Auth Service Provider\n * @package @jmruthers/pace-core\n * @module Providers/Services\n * @since 0.1.0\n *\n * React provider for AuthService.\n * Provides authentication service instance to React components.\n */\n\nimport React, { createContext, useMemo, useEffect, useState } from 'react';\nimport { type SupabaseClient } from '@supabase/supabase-js';\nimport { AuthService } from '../../services/AuthService';\nimport type { SessionRestorationState } from '../../types/auth';\nimport { logger } from '../../utils/core/logger';\n\n// Context type\nexport interface AuthServiceContextType {\n authService: AuthService;\n sessionRestoration: SessionRestorationState;\n}\n\nexport const AuthServiceContext = createContext<AuthServiceContextType | null>(null);\n\nexport interface AuthServiceProviderProps {\n children: React.ReactNode;\n supabaseClient: SupabaseClient;\n appName?: string;\n}\n\nexport function AuthServiceProvider({ children, supabaseClient, appName }: AuthServiceProviderProps) {\n // Create service instance with useMemo to prevent recreation on every render\n const authService = useMemo(\n () => new AuthService(supabaseClient, appName),\n [supabaseClient, appName]\n );\n\n const [sessionRestoration, setSessionRestoration] = useState<SessionRestorationState>(\n () => authService.getSessionRestorationState()\n );\n\n // Subscribe to service updates to keep restoration state in sync\n useEffect(() => {\n const unsubscribe = authService.subscribe(() => {\n const restorationState = authService.getSessionRestorationState();\n setSessionRestoration(restorationState);\n });\n\n return () => {\n unsubscribe();\n };\n }, [authService]);\n\n // Initialize service on mount\n useEffect(() => {\n authService.initialize().catch(error => {\n logger.error('AuthServiceProvider', 'Failed to initialize auth service:', error);\n });\n\n // Cleanup on unmount\n return () => {\n authService.cleanup();\n };\n }, [authService]);\n\n const contextValue = useMemo(() => ({\n authService,\n sessionRestoration\n }), [authService, sessionRestoration]);\n\n return (\n <AuthServiceContext.Provider value={contextValue}>\n {children}\n </AuthServiceContext.Provider>\n );\n}","/**\n * @file Authentication Service\n * @package @jmruthers/pace-core\n * @module Services\n * @since 0.1.0\n *\n * Authentication service implementation.\n * Handles user authentication, session management, and auth-related operations.\n */\n\nimport { type SupabaseClient, type User, type Session, AuthError, type AuthChangeEvent, type Session as SupabaseSession } from '@supabase/supabase-js';\nimport type { SessionRestorationState } from '../types/auth';\nimport { BaseService } from './base/BaseService';\nimport { IAuthService, AuthResult } from './interfaces/IAuthService';\nimport { logger } from '../utils/core/logger';\n\ntype AuthStateSubscription = {\n data: {\n subscription: {\n unsubscribe: () => void;\n };\n };\n};\n\nexport class AuthService extends BaseService implements IAuthService {\n private user: User | null = null;\n private session: Session | null = null;\n private authLoading = false;\n private authError: AuthError | null = null;\n private supabaseClient: SupabaseClient | null = null;\n private authStateSubscription: { unsubscribe: () => void } | null = null;\n private sessionRestorationState: SessionRestorationState = {\n isRestoring: false,\n restorationComplete: false,\n restorationError: null,\n };\n private restorationTimeoutId: ReturnType<typeof setTimeout> | null = null;\n private readonly restorationTimeoutMs = 5000;\n private restorationStartTime: number | null = null;\n private appName: string | undefined = undefined;\n private errorHandler: ((event: ErrorEvent) => void) | null = null;\n private unhandledRejectionHandler: ((event: PromiseRejectionEvent) => void) | null = null;\n\n constructor(supabaseClient: SupabaseClient, appName?: string) {\n super();\n this.supabaseClient = supabaseClient;\n this.appName = appName;\n }\n\n // Auth state getters\n getUser(): User | null {\n return this.user;\n }\n\n getSession(): Session | null {\n return this.session;\n }\n\n isAuthenticated(): boolean {\n return !!(this.user && this.session);\n }\n\n isLoading(): boolean {\n return this.authLoading;\n }\n\n getError(): AuthError | null {\n return this.authError;\n }\n\n getSupabaseClient(): SupabaseClient | null {\n return this.supabaseClient;\n }\n\n getSessionRestorationState(): SessionRestorationState {\n return { ...this.sessionRestorationState };\n }\n\n // Auth methods\n async signIn(email: string, password?: string): Promise<AuthResult> {\n if (!this.supabaseClient) {\n const error = new AuthError('Supabase client not available');\n this.authError = error;\n this.notify();\n return { user: null, session: null, error };\n }\n\n try {\n const { data, error } = await this.supabaseClient.auth.signInWithPassword({\n email,\n password: password || '',\n });\n \n if (error) {\n this.authError = error;\n this.user = null;\n this.session = null;\n } else {\n this.authError = null;\n this.user = data.user;\n this.session = data.session;\n }\n \n this.notify();\n return { user: data.user, session: data.session, error };\n } catch (error) {\n // Convert regular Error to AuthError if needed\n const authError = error instanceof AuthError \n ? error \n : new AuthError(error instanceof Error ? error.message : 'Authentication failed');\n this.authError = authError;\n this.user = null;\n this.session = null;\n this.notify();\n return { user: null, session: null, error: authError };\n }\n }\n\n async signUp(email: string, password: string): Promise<AuthResult> {\n if (!this.supabaseClient) {\n const error = new AuthError('Supabase client not available');\n this.authError = error;\n this.notify();\n return { user: null, session: null, error };\n }\n\n try {\n const { data, error } = await this.supabaseClient.auth.signUp({\n email,\n password,\n });\n \n if (error) {\n this.authError = error;\n this.user = null;\n this.session = null;\n } else {\n this.authError = null;\n this.user = data.user;\n this.session = data.session;\n }\n \n this.notify();\n return { user: data.user, session: data.session, error };\n } catch (error) {\n // Convert regular Error to AuthError if needed\n const authError = error instanceof AuthError \n ? error \n : new AuthError(error instanceof Error ? error.message : 'Authentication failed');\n this.authError = authError;\n this.user = null;\n this.session = null;\n this.notify();\n return { user: null, session: null, error: authError };\n }\n }\n\n async signOut(): Promise<AuthResult> {\n if (!this.supabaseClient) {\n const error = new AuthError('Supabase client not available');\n this.authError = error;\n this.notify();\n return { user: null, session: null, error };\n }\n\n try {\n const { error } = await this.supabaseClient.auth.signOut();\n \n if (error) {\n this.authError = error;\n } else {\n this.authError = null;\n this.user = null;\n this.session = null;\n }\n \n this.notify();\n return { user: null, session: null, error };\n } catch (error) {\n // Convert regular Error to AuthError if needed\n const authError = error instanceof AuthError \n ? error \n : new AuthError(error instanceof Error ? error.message : 'Authentication failed');\n this.authError = authError;\n this.user = null;\n this.session = null;\n this.notify();\n return { user: null, session: null, error: authError };\n }\n }\n\n async resetPassword(email: string): Promise<AuthResult> {\n if (!this.supabaseClient) {\n const error = new AuthError('Supabase client not available');\n this.authError = error;\n this.notify();\n return { user: null, session: null, error };\n }\n\n try {\n const { error } = await this.supabaseClient.auth.resetPasswordForEmail(email);\n \n if (error) {\n this.authError = error;\n } else {\n this.authError = null;\n }\n \n this.notify();\n return { user: null, session: null, error };\n } catch (error) {\n // Convert regular Error to AuthError if needed\n const authError = error instanceof AuthError \n ? error \n : new AuthError(error instanceof Error ? error.message : 'Authentication failed');\n this.authError = authError;\n this.notify();\n return { user: null, session: null, error: authError };\n }\n }\n\n async updatePassword(password: string): Promise<AuthResult> {\n if (!this.supabaseClient) {\n const error = new AuthError('Supabase client not available');\n this.authError = error;\n this.notify();\n return { user: null, session: null, error };\n }\n\n try {\n const { error } = await this.supabaseClient.auth.updateUser({\n password,\n });\n \n if (error) {\n this.authError = error;\n } else {\n this.authError = null;\n }\n \n this.notify();\n return { user: null, session: null, error };\n } catch (error) {\n const authError = error as AuthError;\n this.authError = authError;\n this.notify();\n return { user: null, session: null, error: authError };\n }\n }\n\n async refreshSession(): Promise<AuthResult> {\n if (!this.supabaseClient) {\n const error = new AuthError('Supabase client not available');\n this.authError = error;\n this.notify();\n return { user: null, session: null, error };\n }\n\n try {\n const { data, error } = await this.supabaseClient.auth.refreshSession();\n \n if (error) {\n this.authError = error;\n this.user = null;\n this.session = null;\n } else {\n this.authError = null;\n // Only update user and session if both are present (valid session)\n if (data?.user && data?.session) {\n this.user = data.user;\n this.session = data.session;\n } else {\n // If no valid session, clear everything\n this.user = null;\n this.session = null;\n }\n }\n \n this.notify();\n // Only return user if we have a valid session\n return { \n user: (data?.user && data?.session) ? data.user : null, \n session: data?.session ?? null, \n error \n };\n } catch (error) {\n // Convert regular Error to AuthError if needed\n const authError = error instanceof AuthError \n ? error \n : new AuthError(error instanceof Error ? error.message : 'Authentication failed');\n this.authError = authError;\n this.user = null;\n this.session = null;\n this.notify();\n return { user: null, session: null, error: authError };\n }\n }\n\n // Lifecycle methods\n async initialize(): Promise<void> {\n await super.initialize();\n // Set loading to true before starting session restoration\n // This ensures ProtectedRoute shows loading state while session is being restored\n this.authLoading = true;\n this.notify();\n \n // Setup auth state listener first - this will receive INITIAL_SESSION event\n await this.setupAuthStateListener();\n \n // Then restore session - this will trigger INITIAL_SESSION event if session exists\n await this.restoreSession();\n }\n\n cleanup(): void {\n if (this.authStateSubscription) {\n this.authStateSubscription.unsubscribe();\n this.authStateSubscription = null;\n }\n this.clearRestorationTimeout();\n this.restorationStartTime = null;\n this.sessionRestorationState = {\n isRestoring: false,\n restorationComplete: false,\n restorationError: null,\n };\n this.authLoading = false;\n super.cleanup();\n }\n\n protected async doInitialize(): Promise<void> {\n // Setup global error handlers\n this.setupErrorHandlers();\n }\n\n protected doCleanup(): void {\n // Remove global error handlers\n this.removeErrorHandlers();\n }\n\n private startSessionRestoration(): void {\n this.clearRestorationTimeout();\n this.sessionRestorationState = {\n isRestoring: true,\n restorationComplete: false,\n restorationError: null,\n };\n this.authLoading = true;\n this.restorationStartTime = Date.now();\n this.notify();\n\n this.restorationTimeoutId = setTimeout(() => {\n logger.warn('AuthService', 'Session restoration timed out after', this.restorationTimeoutMs, 'ms');\n const timeoutError = new Error(`Session restoration timed out after ${this.restorationTimeoutMs}ms`);\n timeoutError.name = 'SessionRestorationTimeoutError';\n this.finishSessionRestoration(timeoutError);\n }, this.restorationTimeoutMs);\n }\n\n private finishSessionRestoration(error?: Error): void {\n if (!this.sessionRestorationState.isRestoring && !error) {\n return;\n }\n\n this.clearRestorationTimeout();\n const completedAt = Date.now();\n const duration = this.restorationStartTime ? completedAt - this.restorationStartTime : null;\n this.restorationStartTime = null;\n const restorationComplete = !error;\n this.sessionRestorationState = {\n isRestoring: false,\n restorationComplete,\n restorationError: error ?? null,\n };\n this.authLoading = false;\n\n if (error) {\n logger.warn('AuthService', 'Session restoration finished with error:', error);\n }\n\n this.notify();\n }\n\n private clearRestorationTimeout(): void {\n if (this.restorationTimeoutId) {\n clearTimeout(this.restorationTimeoutId);\n this.restorationTimeoutId = null;\n }\n }\n\n private async setupAuthStateListener(): Promise<void> {\n if (!this.supabaseClient) {\n this.authLoading = false;\n this.notify();\n return;\n }\n\n try {\n const subscription = this.supabaseClient.auth.onAuthStateChange(\n (event: AuthChangeEvent, session: SupabaseSession | null) => {\n try {\n // Handle different auth events\n if (event === 'SIGNED_OUT') {\n this.session = null;\n this.user = null;\n this.authError = null;\n \n // Automatic session tracking (non-blocking)\n if (session?.user) {\n this.trackSession('logout', session).catch(err => {\n logger.warn('AuthService', 'Failed to track logout session:', err);\n });\n }\n } else if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {\n this.session = session;\n this.user = session?.user ?? null;\n\n // Only clear auth error if we have a valid session\n if (session) {\n this.authError = null;\n }\n \n // Automatic session tracking for login (non-blocking)\n // Only track on SIGNED_IN, not TOKEN_REFRESHED (to avoid duplicate login records)\n if (event === 'SIGNED_IN' && session?.user) {\n this.trackSession('login', session).catch(err => {\n logger.warn('AuthService', 'Failed to track login session:', err);\n });\n }\n } else if (event === 'INITIAL_SESSION') {\n if (session) {\n this.session = session;\n this.user = session.user ?? null;\n this.authError = null;\n \n // Reset restoration state if valid session arrives after earlier failure\n // This clears stale errors when session eventually succeeds\n const hasTimeoutError = this.sessionRestorationState.restorationError?.name === 'SessionRestorationTimeoutError';\n if (this.sessionRestorationState.isRestoring || \n this.sessionRestorationState.restorationError ||\n (hasTimeoutError && session)) {\n this.finishSessionRestoration();\n }\n } else {\n // No session in INITIAL_SESSION event - user is not authenticated\n // Finish restoration to clear loading state\n if (this.sessionRestorationState.isRestoring) {\n this.finishSessionRestoration();\n }\n }\n\n // CRITICAL: Set loading to false AFTER handling INITIAL_SESSION\n // This ensures ProtectedRoute waits for session restoration to complete\n // before checking authentication state\n this.authLoading = false;\n this.notify();\n return; // Return early to avoid setting loading to false again below\n }\n\n // For other events (SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED), set loading to false\n // INITIAL_SESSION is handled above and returns early\n this.authLoading = false;\n this.notify();\n } catch (error) {\n logger.warn('AuthService', 'Error in auth state change handler:', error);\n this.authLoading = false;\n this.notify();\n }\n }\n );\n this.authStateSubscription = subscription.data.subscription;\n } catch (error) {\n logger.error('AuthService', 'Failed to setup auth state listener:', error);\n throw error; // Re-throw to propagate to the provider\n }\n }\n\n private async restoreSession(): Promise<void> {\n if (!this.supabaseClient) {\n const error = new Error('Supabase client not available during session restoration');\n logger.error('AuthService', 'Unable to restore session:', error);\n this.finishSessionRestoration(error);\n return;\n }\n\n this.startSessionRestoration();\n\n try {\n let currentSession: Session | null = null;\n let sessionError: AuthError | null = null;\n\n try {\n const { data, error } = await this.supabaseClient.auth.getSession();\n currentSession = data?.session ?? null;\n sessionError = error ?? null;\n } catch (error) {\n // Handle cases where getSession might not exist (mocked/test clients)\n currentSession = null;\n sessionError = null;\n }\n\n if (sessionError) {\n // Record error but continue to attempt getUser to satisfy edge cases\n this.authError = sessionError;\n \n // Attempt getUser as fallback when getSession fails\n try {\n const { data, error } = await this.supabaseClient.auth.getUser();\n const currentUser = data?.user ?? null;\n const userError = error ?? null;\n \n if (currentUser) {\n this.user = currentUser;\n // If we got a user but no session, we still don't have a valid session\n this.session = null;\n }\n if (userError && !this.authError) {\n this.authError = userError;\n }\n } catch (getUserError) {\n // If getUser also fails, we've already recorded the sessionError\n }\n }\n\n if (currentSession) {\n this.session = currentSession;\n this.user = currentSession.user;\n this.authError = null;\n } else if (!sessionError) {\n // Only skip getUser if we didn't already attempt it due to a sessionError\n // Treat missing session as a normal cold-start state on public pages (e.g., login)\n // Do not call getUser() which can raise AuthSessionMissingError and surface a noisy banner\n // No active session found; treating as normal unauthenticated state\n this.session = null;\n this.user = null;\n this.authError = null;\n }\n\n // CRITICAL FIX: Don't finish restoration here - wait for INITIAL_SESSION event\n // The INITIAL_SESSION event should fire when the auth state listener is set up\n // However, if it doesn't fire within a short delay (e.g., edge case), we'll finish it\n // This ensures ProtectedRoute waits for the event before checking auth state\n \n // Set a short fallback timeout to finish restoration if INITIAL_SESSION doesn't fire\n // This handles edge cases where the event might be delayed or not fire\n setTimeout(() => {\n // Only finish if restoration is still in progress and INITIAL_SESSION hasn't fired\n // The INITIAL_SESSION handler will have set restorationComplete to true if it fired\n if (this.sessionRestorationState.isRestoring && !this.sessionRestorationState.restorationComplete) {\n logger.debug('AuthService', 'INITIAL_SESSION event did not fire, finishing restoration');\n this.finishSessionRestoration();\n }\n }, 100); // 100ms fallback - INITIAL_SESSION should fire immediately when listener is set up\n } catch (error) {\n const restorationError = error instanceof Error\n ? error\n : new Error('Unknown error during auth initialization');\n logger.error('AuthService', 'Error during auth initialization:', restorationError);\n if (restorationError instanceof AuthError) {\n this.authError = restorationError;\n }\n this.finishSessionRestoration(restorationError);\n }\n }\n\n /**\n * Automatically track user session using rbac_session_track\n * This method is called automatically on SIGNED_IN and SIGNED_OUT events.\n * It's non-blocking and failures are logged as warnings.\n */\n private async trackSession(\n sessionType: 'login' | 'logout',\n session: Session | null\n ): Promise<void> {\n if (!this.supabaseClient || !session?.user) {\n return;\n }\n\n try {\n // Resolve app_id from appName if available\n let appId: string | undefined = undefined;\n if (this.appName) {\n const { data, error } = await this.supabaseClient\n .from('rbac_apps')\n .select('id')\n .eq('name', this.appName)\n .eq('is_active', true)\n .single();\n \n if (!error && data) {\n appId = data.id;\n }\n }\n\n // Get IP address and user agent from browser (if available)\n const ipAddress = undefined; // Browser doesn't expose IP directly, could use API\n const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : undefined;\n \n // Get device fingerprint from localStorage if available\n // Note: Device fingerprinting should be done by consuming app and passed via custom header\n // For now, we'll skip it to avoid dependencies\n const deviceFingerprint = undefined;\n\n // Call rbac_session_track RPC function\n // This automatically inserts into rbac_user_sessions AND rbac_user_login_history (for login)\n // Note: Using type assertion for RPC call as Supabase doesn't provide full typing for custom RPC functions\n const { error } = await (this.supabaseClient as unknown as { rpc: (name: string, params: Record<string, unknown>) => Promise<{ error: Error | null }> }).rpc('rbac_session_track', {\n p_user_id: session.user.id,\n p_session_type: sessionType,\n p_event_id: null, // Event ID should come from context, not auth service\n p_app_id: appId,\n p_ip_address: ipAddress,\n p_user_agent: userAgent,\n p_device_fingerprint: deviceFingerprint,\n });\n\n if (error) {\n logger.warn('AuthService', `Failed to track ${sessionType} session:`, error);\n }\n } catch (error) {\n // Log error but don't throw (non-blocking)\n logger.warn('AuthService', `Error tracking ${sessionType} session:`, error);\n }\n }\n\n private setupErrorHandlers(): void {\n if (typeof window === 'undefined') return;\n\n this.errorHandler = (event: ErrorEvent) => {\n if (event.error?.message?.includes('AuthSessionMissingError') || \n event.error?.message?.includes('Auth session missing')) {\n logger.warn('AuthService', 'Suppressing AuthSessionMissingError during logout');\n event.preventDefault();\n return false;\n }\n };\n\n this.unhandledRejectionHandler = (event: PromiseRejectionEvent) => {\n if (event.reason?.message?.includes('AuthSessionMissingError') ||\n event.reason?.message?.includes('Auth session missing')) {\n logger.warn('AuthService', 'Suppressing unhandled AuthSessionMissingError');\n event.preventDefault();\n return false;\n }\n };\n\n window.addEventListener('error', this.errorHandler);\n window.addEventListener('unhandledrejection', this.unhandledRejectionHandler);\n }\n\n private removeErrorHandlers(): void {\n if (typeof window === 'undefined') return;\n\n if (this.errorHandler) {\n window.removeEventListener('error', this.errorHandler);\n this.errorHandler = null;\n }\n\n if (this.unhandledRejectionHandler) {\n window.removeEventListener('unhandledrejection', this.unhandledRejectionHandler);\n this.unhandledRejectionHandler = null;\n }\n }\n}","/**\n * @file Base Service Class\n * @package @jmruthers/pace-core\n * @module Services/Base\n * @since 0.1.0\n *\n * Base service class implementing the observable pattern for React subscriptions.\n * All services extend this class to provide state change notifications.\n */\n\nimport { logger } from '../../utils/core/logger';\n\nexport type StateChangeCallback = () => void;\n\nexport abstract class BaseService {\n private subscribers: Array<StateChangeCallback> = [];\n private isInitialized = false;\n\n /**\n * Subscribe to state changes\n * @param callback Function to call when state changes\n * @returns Unsubscribe function\n */\n subscribe(callback: StateChangeCallback): () => void {\n this.subscribers.push(callback);\n \n // Return unsubscribe function that removes only the first occurrence\n return () => {\n const index = this.subscribers.indexOf(callback);\n if (index > -1) {\n this.subscribers.splice(index, 1);\n }\n };\n }\n\n /**\n * Notify all subscribers of state changes\n * This triggers React re-renders\n */\n protected notify(): void {\n this.subscribers.forEach(callback => {\n try {\n callback();\n } catch (error) {\n logger.error('BaseService', 'Error in subscriber callback:', error);\n }\n });\n }\n\n /**\n * Initialize the service\n * Override in subclasses to implement initialization logic\n */\n async initialize(): Promise<void> {\n if (this.isInitialized) {\n return;\n }\n \n await this.doInitialize();\n this.isInitialized = true;\n }\n\n /**\n * Cleanup the service\n * Override in subclasses to implement cleanup logic\n */\n cleanup(): void {\n this.subscribers = [];\n this.doCleanup();\n this.isInitialized = false;\n }\n\n /**\n * Check if service is initialized\n */\n protected getInitialized(): boolean {\n return this.isInitialized;\n }\n\n /**\n * Reset initialization state (allows re-initialization)\n * Use when dependencies change and service needs to re-initialize\n */\n protected resetInitialization(): void {\n this.isInitialized = false;\n }\n\n /**\n * Override in subclasses to implement initialization logic\n */\n protected abstract doInitialize(): Promise<void>;\n\n /**\n * Override in subclasses to implement cleanup logic\n */\n protected abstract doCleanup(): void;\n}","/**\n * @file Organisation Service Provider\n * @package @jmruthers/pace-core\n * @module Providers/Services\n * @since 0.1.0\n *\n * React provider for OrganisationService.\n * Provides organisation service instance to React components.\n */\n\nimport React, { createContext, useMemo, useEffect, useRef } from 'react';\nimport { type SupabaseClient, type User, type Session } from '@supabase/supabase-js';\nimport { OrganisationService } from '../../services/OrganisationService';\nimport { logger } from '../../utils/core/logger';\n\n// Context type\nexport interface OrganisationServiceContextType {\n organisationService: OrganisationService;\n}\n\nexport const OrganisationServiceContext = createContext<OrganisationServiceContextType | null>(null);\n\nexport interface OrganisationServiceProviderProps {\n children: React.ReactNode;\n supabaseClient: SupabaseClient;\n user: User | null;\n session: Session | null;\n}\n\nexport function OrganisationServiceProvider({ \n children, \n supabaseClient, \n user, \n session \n}: OrganisationServiceProviderProps) {\n // Create service instance once with useRef to avoid recreation on auth state changes\n const organisationServiceRef = useRef<OrganisationService | null>(null);\n \n if (!organisationServiceRef.current) {\n organisationServiceRef.current = new OrganisationService(supabaseClient, user, session);\n }\n \n const organisationService = organisationServiceRef.current;\n\n // Update service dependencies when they change without recreation\n useEffect(() => {\n organisationService.updateDependencies(user, session);\n \n // Re-initialize service when user/session changes\n let isMounted = true;\n \n organisationService.initialize()\n .catch(error => {\n if (isMounted) {\n // \"User has no access to active organisations\" is a valid state for users without orgs (e.g., profile pages)\n // Log it as a warning instead of an error to reduce noise\n const errorMessage = error instanceof Error ? error.message : String(error);\n if (errorMessage === 'User has no access to active organisations') {\n logger.warn('OrganisationServiceProvider', 'User has no active organisations (this is expected for users without organisation access):', error);\n } else {\n logger.error('OrganisationServiceProvider', 'Failed to initialize organisation service:', error);\n }\n }\n });\n\n return () => {\n isMounted = false;\n };\n }, [organisationService, user, session]);\n\n // Cleanup service on unmount only\n useEffect(() => {\n return () => {\n organisationService.cleanup();\n };\n }, [organisationService]);\n\n const contextValue = useMemo(() => ({\n organisationService\n }), [organisationService]);\n\n return (\n <OrganisationServiceContext.Provider value={contextValue}>\n {children}\n </OrganisationServiceContext.Provider>\n );\n}","/**\n * @file Organisation Service\n * @package @jmruthers/pace-core\n * @module Services\n * @since 0.1.0\n *\n * Organisation service implementation.\n * Handles organisation management and selection with security-first approach.\n */\n\nimport { type SupabaseClient, type User, type Session } from '@supabase/supabase-js';\nimport { BaseService } from './base/BaseService';\nimport { IOrganisationService } from './interfaces/IOrganisationService';\nimport type {\n Organisation,\n OrganisationMembership,\n OrganisationSecurityError,\n OrganisationHierarchy\n} from '../types/organisation';\nimport { setOrganisationContext } from '../utils/context/organisationContext';\nimport { logger } from '../utils/core/logger';\nimport { assertUserId, assertOrganisationId } from '../types/core';\nimport { isSuperAdmin } from '../rbac/api';\nimport type { UUID } from '../rbac/types';\n\n// Type for RPC response from data_user_organisation_roles_get\ninterface OrganisationRoleRpcResponse {\n user_id: string;\n organisation_id: string;\n role: 'org_admin' | 'leader' | 'member' | 'supporter';\n status: 'active' | 'inactive' | 'suspended';\n // Organisation fields from RPC\n name?: string;\n display_name?: string;\n subscription_tier?: string;\n settings?: unknown;\n is_active?: boolean;\n parent_id?: string;\n organisation_created_at?: string;\n organisation_updated_at?: string;\n [key: string]: unknown;\n}\n\nexport class OrganisationService extends BaseService implements IOrganisationService {\n private _selectedOrganisation: Organisation | null = null;\n private _organisations: Organisation[] = [];\n private _userMemberships: OrganisationMembership[] = [];\n private _roleMapState: Map<string, string> = new Map();\n private _isLoading = false;\n private _error: Error | null = null;\n private _isSuperAdmin: boolean = false; // Cache super admin status\n private _isContextReady = false;\n private retryCount = 0;\n\n // Dependencies\n private supabaseClient: SupabaseClient | null = null;\n private user: User | null = null;\n private session: Session | null = null;\n\n // Internal state management\n private isLoadingRef = false;\n private lastLoadTimeRef = 0;\n private hasFailedRef = false;\n private abortControllerRef: AbortController | null = null;\n\n constructor(supabaseClient: SupabaseClient, user: User | null, session: Session | null) {\n super();\n this.supabaseClient = supabaseClient;\n this.user = user;\n this.session = session;\n }\n\n // Interface implementation\n getSelectedOrganisation(): Organisation | null { return this._selectedOrganisation; }\n getOrganisations(): Organisation[] { return this._organisations; }\n getUserMemberships(): OrganisationMembership[] { return this._userMemberships; }\n isLoading(): boolean { \n return this._isLoading;\n }\n getError(): Error | null { return this._error; }\n hasValidOrganisationContext(): boolean { return !!(this._selectedOrganisation && !this._isLoading && !this._error && this._isContextReady); }\n isContextReady(): boolean { return this._isContextReady; }\n\n // Additional methods for testing\n setSelectedOrganisation(organisation: Organisation | null): void {\n // SECURITY: Validate organisation is in user's accessible organisations (only if orgs are loaded)\n // Exception: Super admins can set any organisation (they have global access)\n if (organisation && this._organisations.length > 0) {\n // Only validate if user is not a super admin (use cached status)\n if (!this._isSuperAdmin) {\n const isValidOrg = this._organisations.some(org => org.id === organisation.id);\n if (!isValidOrg) {\n logger.warn('OrganisationService', 'Attempted to set invalid organisation - not in user\\'s accessible organisations', {\n organisationId: organisation.id,\n organisationName: organisation.name,\n accessibleOrgIds: this._organisations.map(o => o.id)\n });\n // Don't set invalid organisation - this prevents security issues\n // If organisations haven't loaded yet, validation will happen in loadUserOrganisations()\n return;\n }\n }\n }\n \n this._selectedOrganisation = organisation;\n if (organisation) {\n localStorage.setItem('pace-core-selected-organisation', JSON.stringify(organisation));\n this.setDatabaseOrganisationContext(organisation);\n } else {\n localStorage.removeItem('pace-core-selected-organisation');\n this._isContextReady = false;\n }\n this.notify();\n }\n\n // For testing: expose dependencies\n getDependencies(): { user: User | null; session: Session | null; supabaseClient: SupabaseClient | null } {\n return {\n user: this.user,\n session: this.session,\n supabaseClient: this.supabaseClient\n };\n }\n\n // For testing: manually set state\n setTestState(\n organisations: Organisation[],\n memberships: OrganisationMembership[],\n roleMap: Map<string, string>,\n selectedOrg: Organisation | null = null\n ): void {\n this._organisations = organisations;\n this._userMemberships = memberships;\n this._roleMapState = roleMap;\n if (selectedOrg) {\n this._selectedOrganisation = selectedOrg;\n } else if (organisations.length > 0) {\n this._selectedOrganisation = organisations[0];\n }\n this._isLoading = false;\n this._error = null;\n this.notify();\n }\n\n // Update dependencies\n updateDependencies(user: User | null, session: Session | null): void {\n const wasAuthenticated = !!(this.user && this.session);\n const isAuthenticated = !!(user && session);\n \n // Reset super admin cache when user changes\n if (this.user?.id !== user?.id) {\n this._isSuperAdmin = false;\n }\n \n this.user = user;\n this.session = session;\n \n // If user logs out, allow re-initialization when they log back in\n if (wasAuthenticated && !isAuthenticated) {\n // Reset BaseService initialization state to allow re-initialization\n this.resetInitialization();\n }\n \n this.notify();\n }\n\n // Organisation methods\n async switchOrganisation(orgId: string): Promise<void> {\n // Validate access\n if (!this.validateOrganisationAccess(orgId)) {\n throw new Error(`User does not have access to organisation ${orgId}`) as OrganisationSecurityError;\n }\n \n const targetOrg = this._organisations.find(org => org.id === orgId);\n if (!targetOrg) {\n throw new Error(`Organisation ${orgId} not found in user's organisations`) as OrganisationSecurityError;\n }\n \n this._selectedOrganisation = targetOrg;\n \n // Persist selection\n localStorage.setItem('pace-core-selected-organisation', JSON.stringify(targetOrg));\n \n // Set database organisation context\n await this.setDatabaseOrganisationContext(targetOrg);\n \n this.notify();\n }\n\n getUserRole(orgId?: string): string {\n const targetOrgId = orgId || this._selectedOrganisation?.id;\n if (!targetOrgId) return 'no_access';\n \n // Use roleMapState to get the role for this organisation\n return this._roleMapState.get(targetOrgId) || 'no_access';\n }\n\n validateOrganisationAccess(orgId: string): boolean {\n return this._userMemberships.some((m) => \n m.organisation_id === orgId && \n m.status === 'active' &&\n m.revoked_at === null\n );\n }\n\n async refreshOrganisations(): Promise<void> {\n if (!this.user || !this.session || !this.supabaseClient) return;\n \n // Force reload by triggering the effect\n this._isLoading = true;\n this.notify();\n await this.loadUserOrganisations();\n }\n\n ensureOrganisationContext(): Organisation {\n if (!this._selectedOrganisation) {\n throw new Error('Organisation context is required but not available') as OrganisationSecurityError;\n }\n return this._selectedOrganisation;\n }\n\n isOrganisationSecure(): boolean {\n return !!(this._selectedOrganisation && this.user);\n }\n\n getPrimaryOrganisation(): Organisation | null {\n // Look for org_admin role first, then leader, then member\n const rolePriority = ['org_admin', 'leader', 'member'] as const;\n \n for (const role of rolePriority) {\n const membership = this._userMemberships.find((m) => m.role === role);\n if (membership) {\n return this._organisations.find((org) => org.id === membership.organisation_id) || null;\n }\n }\n \n return null;\n }\n\n buildOrganisationHierarchy(orgs: Organisation[]): OrganisationHierarchy[] {\n const orgMap = new Map<string, Organisation>();\n orgs.forEach(org => orgMap.set(org.id, org));\n \n const roots: OrganisationHierarchy[] = [];\n \n orgs.forEach(org => {\n if (!org.parent_id) {\n // Root organisation\n roots.push({\n organisation: org,\n children: [],\n depth: 0\n });\n }\n });\n \n // For now, return flat structure - hierarchy building can be added later\n return roots;\n }\n\n // Lifecycle methods\n async initialize(): Promise<void> {\n await super.initialize();\n \n // Don't load if already loading (prevents duplicate loads during rapid auth events)\n if (!this.isLoadingRef) {\n await this.loadUserOrganisations();\n }\n }\n\n cleanup(): void {\n // Cleanup on unmount\n this.isLoadingRef = false;\n this.hasFailedRef = false;\n this.lastLoadTimeRef = 0;\n // Don't abort pending requests - let them complete naturally\n // Aborting causes React StrictMode issues where requests are cancelled mid-flight\n // The requests will complete on their own and update state if needed\n if (this.abortControllerRef) {\n // Clear the reference but don't abort - let requests complete\n this.abortControllerRef = null;\n }\n // Reset state\n this._selectedOrganisation = null;\n this._organisations = [];\n this._userMemberships = [];\n this._roleMapState = new Map();\n this._isLoading = false;\n this._error = null;\n this._isContextReady = false;\n super.cleanup();\n }\n\n protected async doInitialize(): Promise<void> {\n // Initial setup\n }\n\n protected doCleanup(): void {\n // Cleanup any resources\n }\n\n private async setDatabaseOrganisationContext(organisation: Organisation): Promise<void> {\n if (!this.supabaseClient || !this.session) {\n logger.warn('OrganisationService', 'No Supabase client or session available for setting organisation context');\n this._isContextReady = false;\n this.notify();\n return;\n }\n\n try {\n // Add timeout to prevent hanging\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => reject(new Error('Context setting timeout after 5 seconds')), 5000);\n });\n \n const contextPromise = setOrganisationContext(this.supabaseClient, organisation.id);\n \n await Promise.race([contextPromise, timeoutPromise]);\n \n // Database organisation context set successfully\n this._isContextReady = true;\n this.notify();\n } catch (error) {\n logger.error('OrganisationService', 'Failed to set database organisation context:', error);\n // Set context ready to true anyway - this is a non-critical operation\n // The app should still work without database context\n this._isContextReady = true;\n this.notify();\n // Don't throw - this is a non-critical operation\n }\n }\n\n private async loadUserOrganisations(): Promise<void> {\n if (!this.user || !this.session || !this.supabaseClient) {\n // Clear state when no user, session, or supabase client\n this._selectedOrganisation = null;\n this._organisations = [];\n this._userMemberships = [];\n this._isLoading = false;\n this._error = null;\n this.notify();\n return;\n }\n\n // Additional check to prevent loading during auth state changes\n if (this.isLoadingRef) {\n // Ensure loading state is correct\n this._isLoading = true;\n this.notify();\n return;\n }\n\n // Prevent rapid retries - minimum 2 seconds between attempts\n const now = Date.now();\n if (now - this.lastLoadTimeRef < 2000) {\n // Ensure loading state is correct\n if (this._organisations.length > 0 || this._selectedOrganisation) {\n this._isLoading = false;\n } else {\n this._isLoading = true;\n }\n this.notify();\n return;\n }\n\n // Cancel any existing request\n if (this.abortControllerRef) {\n this.abortControllerRef.abort();\n }\n\n // Create new abort controller for this request\n this.abortControllerRef = new AbortController();\n const abortSignal = this.abortControllerRef.signal;\n\n this.lastLoadTimeRef = now;\n this.isLoadingRef = true;\n this._isLoading = true;\n this._error = null;\n this.notify();\n \n try {\n // Get user's organisation roles directly from rbac_organisation_roles table\n // This queries the source table directly instead of using the RPC which filters to match core_organisation_memberships view\n // We still filter to active, non-revoked roles for the org selector\n // The join includes organisation data, so we don't need a separate query that might be filtered by RLS\n let memberships, membershipError, organisations: Organisation[] = [];\n try {\n // Check if request was aborted before making query\n if (abortSignal.aborted) {\n throw new Error('Request aborted');\n }\n\n const { data: rolesData, error: rolesError } = await this.supabaseClient\n .from('rbac_organisation_roles')\n .select(`\n id,\n user_id,\n organisation_id,\n role,\n status,\n granted_at,\n granted_by,\n revoked_at,\n revoked_by,\n notes,\n created_at,\n updated_at,\n core_organisations!inner(\n id,\n name,\n display_name,\n subscription_tier,\n settings,\n is_active,\n parent_id,\n created_at,\n updated_at\n )\n `)\n .eq('user_id', this.user.id)\n .eq('status', 'active')\n .is('revoked_at', null);\n \n if (rolesError) {\n logger.error(\"OrganisationService\", \"Error loading organisation roles:\", rolesError);\n throw rolesError;\n }\n \n // Map to branded types and extract organisation data from the join\n // The join already includes organisation data, so we don't need a separate query\n memberships = rolesData?.map((m) => ({\n ...m,\n user_id: assertUserId(m.user_id),\n organisation_id: assertOrganisationId(m.organisation_id),\n })) || [];\n \n // Extract unique organisations from the join results\n // Use a Map to deduplicate by organisation ID\n // Supabase returns joined data nested under the relation name\n const organisationsMap = new Map<string, Organisation>();\n rolesData?.forEach((role: any) => {\n // The join returns organisation data nested under 'core_organisations' key\n const orgData = role.core_organisations;\n if (orgData && role.organisation_id && !organisationsMap.has(role.organisation_id)) {\n organisationsMap.set(role.organisation_id, {\n id: orgData.id,\n name: orgData.name,\n display_name: orgData.display_name,\n subscription_tier: orgData.subscription_tier,\n settings: orgData.settings,\n is_active: orgData.is_active,\n parent_id: orgData.parent_id,\n created_at: orgData.created_at,\n updated_at: orgData.updated_at,\n } as Organisation);\n }\n });\n \n organisations = Array.from(organisationsMap.values());\n \n // Extract organisations from join results\n } catch (queryError) {\n // Extract error message properly from Supabase error objects\n if (queryError instanceof Error) {\n membershipError = queryError;\n } else if (queryError && typeof queryError === 'object' && 'message' in queryError) {\n membershipError = new Error(String((queryError as any).message));\n } else {\n membershipError = new Error(String(queryError));\n }\n logger.error(\"OrganisationService\", \"Error loading organisation roles:\", membershipError);\n throw membershipError;\n }\n \n // Check if user is super admin - super admins don't need organisation memberships\n let userIsSuperAdmin = false;\n if (this.user?.id) {\n try {\n userIsSuperAdmin = await isSuperAdmin(this.user.id as UUID);\n this._isSuperAdmin = userIsSuperAdmin; // Cache the result\n } catch (error) {\n logger.warn('OrganisationService', 'Failed to check super admin status', { error });\n // Continue with normal flow if check fails\n this._isSuperAdmin = false;\n }\n } else {\n this._isSuperAdmin = false;\n }\n\n // Super admins can proceed without organisation memberships\n if (!memberships || memberships.length === 0) {\n if (userIsSuperAdmin) {\n // Super admin without org memberships - allow empty state\n this._organisations = [];\n this._userMemberships = [];\n this._isLoading = false;\n this._error = null;\n this._isContextReady = true;\n this.notify();\n return;\n }\n throw new Error('User has no active organisation memberships') as OrganisationSecurityError;\n }\n\n if (!organisations || organisations.length === 0) {\n if (userIsSuperAdmin) {\n // Super admin without orgs - allow empty state\n this._organisations = [];\n this._userMemberships = [];\n this._isLoading = false;\n this._error = null;\n this._isContextReady = true;\n this.notify();\n return;\n }\n throw new Error('No organisations found in role data') as OrganisationSecurityError;\n }\n\n // Create a map of organisation_id to role from the memberships data\n const roleMap = new Map<string, string>();\n memberships?.forEach((membership) => {\n roleMap.set(membership.organisation_id, membership.role);\n });\n\n // Extract organisations and memberships\n const orgs = organisations as Organisation[];\n const activeOrgs = orgs.filter(org => org.is_active);\n \n // Filter to active organisations only\n \n if (activeOrgs.length === 0) {\n if (userIsSuperAdmin) {\n // Super admin without active orgs - allow empty state\n this._organisations = [];\n this._userMemberships = [];\n this._isLoading = false;\n this._error = null;\n this._isContextReady = true;\n this.notify();\n return;\n }\n throw new Error('User has no access to active organisations') as OrganisationSecurityError;\n }\n\n this._organisations = activeOrgs;\n // Memberships already have branded types from earlier mapping\n this._userMemberships = memberships as OrganisationMembership[];\n \n // Store role map in component state for later use\n this._roleMapState = roleMap;\n \n // Auto-select organisation: try persisted, then primary, then first\n let initialOrg: Organisation | null = null;\n let selectionMethod: 'persisted' | 'admin' | 'first' = 'first';\n \n // 1. Try to restore from localStorage\n try {\n const persistedOrgString = localStorage.getItem('pace-core-selected-organisation');\n if (persistedOrgString) {\n const persistedOrg = JSON.parse(persistedOrgString) as Organisation;\n // Validate persisted org ID before using it\n if (persistedOrg.id && typeof persistedOrg.id === 'string' && persistedOrg.id.trim() !== '') {\n const validPersistedOrg = activeOrgs.find(org => org.id === persistedOrg.id);\n if (validPersistedOrg) {\n initialOrg = validPersistedOrg;\n selectionMethod = 'persisted';\n } else {\n localStorage.removeItem('pace-core-selected-organisation');\n }\n } else {\n localStorage.removeItem('pace-core-selected-organisation');\n }\n }\n } catch (storageError) {\n // Clear potentially corrupted cache\n localStorage.removeItem('pace-core-selected-organisation');\n }\n \n // 2. Fall back to org_admin role organisation (highest privilege)\n if (!initialOrg) {\n const adminMembership = memberships.find((m) => m.role === 'org_admin');\n if (adminMembership) {\n const foundOrg = organisations.find((org) => org.id === adminMembership.organisation_id);\n if (foundOrg) {\n initialOrg = foundOrg;\n selectionMethod = 'admin';\n }\n }\n }\n \n // 3. Fall back to first organisation\n if (!initialOrg) {\n initialOrg = activeOrgs[0];\n selectionMethod = 'first';\n }\n \n if (!initialOrg) {\n throw new Error('No valid organisation found for user') as OrganisationSecurityError;\n }\n\n // SECURITY: Validate current selected organisation is still valid (in case it was set before orgs loaded)\n const currentSelectedOrg = this._selectedOrganisation;\n if (currentSelectedOrg && !activeOrgs.some(org => org.id === currentSelectedOrg.id)) {\n logger.warn('OrganisationService', 'Current selected organisation is no longer valid, resetting', {\n invalidOrgId: currentSelectedOrg.id,\n validOrgIds: activeOrgs.map(o => o.id)\n });\n this._selectedOrganisation = null;\n }\n\n this._selectedOrganisation = initialOrg;\n \n // Persist selection\n localStorage.setItem('pace-core-selected-organisation', JSON.stringify(initialOrg));\n \n // Set database organisation context\n await this.setDatabaseOrganisationContext(initialOrg);\n \n // Reset retry count and failed flag on success\n this.retryCount = 0;\n this.hasFailedRef = false;\n \n } catch (err) {\n const error = err as Error;\n // \"User has no access to active organisations\" is a valid state for users without orgs (e.g., profile pages)\n // Only log actual errors, not expected states\n if (error.message !== 'User has no access to active organisations') {\n logger.error(\"OrganisationService\", \"Failed to load organisations:\", err);\n }\n this._error = error;\n // Increment retry count on error\n this.retryCount = this.retryCount + 1;\n // Set failed flag to prevent further attempts\n this.hasFailedRef = true;\n // Clear all cached data on error to prevent corruption\n this.clearAllCachedData();\n // Mark context as ready even on error - this allows the app to proceed\n // The app can check hasValidOrganisationContext() to determine if org context is available\n this._isContextReady = true;\n } finally {\n // Always cleanup refs and abort controller\n this.isLoadingRef = false;\n this._isLoading = false;\n this.abortControllerRef = null;\n this.notify();\n }\n }\n\n private clearAllCachedData(): void {\n localStorage.removeItem('pace-core-selected-organisation');\n localStorage.removeItem('pace-core-organisation-context');\n this._selectedOrganisation = null;\n this._organisations = [];\n this._userMemberships = [];\n this._roleMapState = new Map();\n this.retryCount = 0;\n this._isContextReady = false;\n // Don't clear _error here - let it persist for error reporting\n }\n}","/**\n * @file Event Service Provider\n * @package @jmruthers/pace-core\n * @module Providers/Services\n * @since 0.1.0\n *\n * React provider for EventService.\n * Provides event service instance to React components.\n */\n\nimport React, { createContext, useMemo, useEffect, useRef } from 'react';\nimport { type SupabaseClient, type User, type Session } from '@supabase/supabase-js';\nimport { EventService } from '../../services/EventService';\nimport { logger } from '../../utils/core/logger';\nimport type { Organisation } from '../../types/organisation';\n\n// Context type\nexport interface EventServiceContextType {\n eventService: EventService;\n}\n\nexport const EventServiceContext = createContext<EventServiceContextType | null>(null);\n\nexport interface EventServiceProviderProps {\n children: React.ReactNode;\n supabaseClient: SupabaseClient;\n user: User | null;\n session: Session | null;\n appName: string;\n selectedOrganisation: Organisation | null;\n setSelectedEventId: (eventId: string | null) => void;\n}\n\nexport function EventServiceProvider({\n children,\n supabaseClient,\n user,\n session,\n appName,\n selectedOrganisation,\n setSelectedEventId\n}: EventServiceProviderProps) {\n // Create service instance once with useRef to avoid recreation on dependency changes\n const eventServiceRef = useRef<EventService | null>(null);\n const initializingRef = useRef(false);\n \n if (!eventServiceRef.current) {\n eventServiceRef.current = new EventService(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId);\n }\n \n const eventService = eventServiceRef.current;\n\n // Update service dependencies and initialize when dependencies change\n // Note: eventService is a ref and never changes, so we don't include it in dependencies\n useEffect(() => {\n let isMounted = true;\n\n if (initializingRef.current) {\n return;\n }\n\n const updateAndInitialize = async () => {\n initializingRef.current = true;\n\n try {\n // Update dependencies (now async to handle user change cleanup)\n await eventService.updateDependencies(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId);\n\n if (!isMounted) return;\n\n // Re-initialize service when dependencies change\n await eventService.initialize().catch(error => {\n if (isMounted) {\n logger.error('EventServiceProvider', 'Failed to initialize event service:', error);\n }\n });\n } finally {\n initializingRef.current = false;\n }\n };\n\n updateAndInitialize();\n\n return () => {\n isMounted = false;\n initializingRef.current = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n // eventService is a ref and never changes, so we exclude it from dependencies\n }, [supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId]);\n\n // Cleanup service on unmount only\n useEffect(() => {\n return () => {\n eventService.cleanup();\n };\n }, [eventService]);\n\n const contextValue = useMemo(() => ({\n eventService\n }), [eventService]);\n\n return (\n <EventServiceContext.Provider value={contextValue}>\n {children}\n </EventServiceContext.Provider>\n );\n}","/**\n * @file Event Service\n * @package @jmruthers/pace-core\n * @module Services\n * @since 0.1.0\n *\n * Event service implementation.\n * Handles event management and selection with organisation context validation.\n */\n\nimport { type SupabaseClient, type User, type Session } from '@supabase/supabase-js';\nimport { BaseService } from './base/BaseService';\nimport { IEventService } from './interfaces/IEventService';\nimport { Event } from '../types/event';\nimport { Organisation } from '../types/organisation';\nimport { assertOrganisationId } from '../types/core';\nimport { logger } from '../utils/core/logger';\nimport { secureStorage } from '../utils/security/secureStorage';\nimport { isSuperAdmin, getAppConfigByName } from '../rbac/api';\nimport type { UUID } from '../rbac/types';\nimport type { AppConfig } from '../rbac/utils/contextValidator';\n\nexport class EventService extends BaseService implements IEventService {\n private events: Event[] = [];\n private selectedEvent: Event | null = null;\n private _isLoading = false; // Start as false to avoid blocking UI\n private error: Error | null = null;\n\n // Dependencies\n private supabaseClient: SupabaseClient | null = null;\n private user: User | null = null;\n private session: Session | null = null;\n private appName: string = '';\n private selectedOrganisation: Organisation | null = null;\n private setSelectedEventId: ((eventId: string | null) => void) | null = null;\n private isSuperAdmin: boolean = false; // Track super admin status for conditional validation\n private appConfig: AppConfig | null = null; // Cache app config to avoid repeated lookups\n\n // Internal state management\n private isInitializedRef = false;\n private isFetchingRef = false;\n private hasAutoSelectedRef = false;\n private userClearedEventRef = false;\n\n constructor(\n supabaseClient: SupabaseClient,\n user: User | null,\n session: Session | null,\n appName: string,\n selectedOrganisation: Organisation | null,\n setSelectedEventId: (eventId: string | null) => void\n ) {\n super();\n this.supabaseClient = supabaseClient;\n this.user = user;\n this.session = session;\n this.appName = appName;\n this.selectedOrganisation = selectedOrganisation;\n this.setSelectedEventId = setSelectedEventId;\n }\n\n // Helper method to get user-scoped storage key\n private getStorageKey(userId: string | null): string {\n if (!userId) {\n // Return a temporary key that won't match any user\n return 'pace-core-selected-event-no-user';\n }\n return `pace-core-selected-event-${userId}`;\n }\n\n // Update dependencies\n async updateDependencies(\n supabaseClient: SupabaseClient,\n user: User | null,\n session: Session | null,\n appName: string,\n selectedOrganisation: Organisation | null,\n setSelectedEventId: (eventId: string | null) => void\n ): Promise<void> {\n const previousOrgId = this.selectedOrganisation?.id;\n const newOrgId = selectedOrganisation?.id;\n const previousUserId = this.user?.id || null;\n const newUserId = user?.id || null;\n const previousAppName = this.appName;\n \n // If user changed, clear previous user's event selection from storage\n if (previousUserId !== newUserId) {\n if (previousUserId !== null) {\n await this.clearEventSelectionForUser(previousUserId);\n }\n // If user is now null (logout), clear current selection state\n if (newUserId === null) {\n this.selectedEvent = null;\n this.setSelectedEventId?.(null);\n }\n // Reset initialization when user changes\n this.resetInitialization();\n this.isInitializedRef = false;\n this.isFetchingRef = false;\n }\n \n this.supabaseClient = supabaseClient;\n this.user = user;\n this.session = session;\n this.appName = appName;\n this.selectedOrganisation = selectedOrganisation;\n this.setSelectedEventId = setSelectedEventId;\n \n // Clear app config cache when app name changes\n if (previousAppName !== appName) {\n this.appConfig = null;\n }\n \n // Update super admin status when user changes\n // This allows super admins to select events from any organisation\n if (user?.id) {\n try {\n this.isSuperAdmin = await isSuperAdmin(user.id as UUID);\n } catch (error) {\n logger.warn('EventService', 'Failed to check super admin status', { error });\n this.isSuperAdmin = false; // Default to false on error\n }\n } else {\n this.isSuperAdmin = false;\n }\n \n // If organisation changed (from null to value, or different org, or value to null), reset initialization\n // This ensures events are re-fetched when organisation context changes\n // For event-required apps, selectedOrganisation will be null, so we need to reset when it changes from undefined/null to null\n if (previousOrgId !== newOrgId) {\n this.resetInitialization(); // Reset BaseService's isInitialized flag\n this.isInitializedRef = false;\n this.isFetchingRef = false;\n // Clear events ONLY when switching between different organisations (not when org first becomes available)\n if (previousOrgId !== null && newOrgId !== null && previousOrgId !== newOrgId) {\n this.events = [];\n this.selectedEvent = null;\n } else if (previousOrgId !== null && newOrgId === null) {\n // Organisation was removed - clear events\n this.events = [];\n this.selectedEvent = null;\n }\n }\n \n this.notify();\n }\n\n // Event state getters\n getEvents(): Event[] {\n // Return stable array reference - only create new array when events actually change\n // This prevents unnecessary re-renders when getEvents() is called on every render\n // The service's notify() mechanism handles change detection\n return this.events;\n }\n\n getSelectedEvent(): Event | null {\n return this.selectedEvent;\n }\n\n isLoading(): boolean {\n return this._isLoading;\n }\n\n getError(): Error | null {\n return this.error;\n }\n\n // Event methods\n setSelectedEvent(event: Event | null): void {\n if (event) {\n // No validation needed: For event-required apps, org is derived from event\n // For org-required apps, event is optional and doesn't need validation\n // RLS policies handle security at the database level\n this.selectedEvent = event;\n this.setSelectedEventId?.(event.event_id);\n // Persist asynchronously (don't await to avoid blocking)\n this.persistEventSelection(event.event_id).catch(error => {\n logger.warn('EventService', 'Failed to persist event selection:', error);\n });\n // Reset the user cleared flag when selecting an event\n this.userClearedEventRef = false;\n } else {\n this.selectedEvent = null;\n this.setSelectedEventId?.(null);\n // Clear from secure storage (don't await to avoid blocking)\n this.clearEventSelection().catch(error => {\n logger.warn('EventService', 'Failed to clear event selection:', error);\n });\n // Reset the auto-selection flag when clearing the event\n this.hasAutoSelectedRef = false;\n // Mark that user explicitly cleared the event to prevent auto-selection\n this.userClearedEventRef = true;\n }\n this.notify();\n }\n\n async refreshEvents(): Promise<void> {\n this.isInitializedRef = false;\n this.isFetchingRef = false;\n // Don't reset the user cleared flag - respect user's choice\n await this.fetchEvents();\n }\n\n async loadPersistedEvent(events: Event[]): Promise<boolean> {\n try {\n const userId = this.user?.id || null;\n \n // Don't load persisted event if no user is authenticated\n if (!userId) {\n return false;\n }\n \n const storageKey = this.getStorageKey(userId);\n \n // Retrieve from secure storage (will automatically decrypt)\n const persistedEventId = await secureStorage.getItem(storageKey);\n \n if (persistedEventId && events.length > 0) {\n // Validate that event exists in user's accessible events\n const persistedEvent = events.find(event => event.event_id === persistedEventId);\n \n if (persistedEvent) {\n // Use setSelectedEvent() to go through same path as EventSelector\n // This ensures consistent behavior and proper notification\n // Theme will be applied by useEventTheme hook once user navigates away from login\n this.setSelectedEvent(persistedEvent);\n return true;\n } else {\n // Event no longer accessible to user, clear invalid persisted event\n await secureStorage.removeItem(storageKey);\n }\n }\n } catch (error) {\n logger.warn('EventService', 'Failed to load persisted event:', error);\n }\n return false;\n }\n\n /**\n * Restore persisted event after login screen has rendered\n * This should be called explicitly from login page component\n * \n * @returns Promise<boolean> - true if event was successfully restored, false otherwise\n */\n async restorePersistedEvent(): Promise<boolean> {\n if (this.events.length === 0) {\n // Events haven't been fetched yet, wait for them\n return false;\n }\n return await this.loadPersistedEvent(this.events);\n }\n\n async persistEventSelection(eventId: string): Promise<void> {\n try {\n const userId = this.user?.id || null;\n const storageKey = this.getStorageKey(userId);\n \n // Store with encryption using secureStorage\n await secureStorage.setItem(storageKey, eventId, { encrypt: true });\n } catch (error) {\n logger.warn('EventService', 'Failed to persist event selection:', error);\n }\n }\n\n async clearEventSelection(): Promise<void> {\n try {\n const userId = this.user?.id || null;\n const storageKey = this.getStorageKey(userId);\n \n // Clear from secure storage\n await secureStorage.removeItem(storageKey);\n \n // Clear the selected event\n this.selectedEvent = null;\n this.setSelectedEventId?.(null);\n } catch (error) {\n logger.warn('EventService', 'Failed to clear event selection:', error);\n }\n }\n\n /**\n * Clear event selection for a specific user (used when user logs out or changes)\n */\n async clearEventSelectionForUser(userId: string | null): Promise<void> {\n try {\n if (!userId) return;\n \n const storageKey = this.getStorageKey(userId);\n await secureStorage.removeItem(storageKey);\n } catch (error) {\n logger.warn('EventService', 'Failed to clear event selection for user:', error);\n }\n }\n\n autoSelectNextEvent(events: Event[]): void {\n const nextEvent = this.getNextEventByDate(events);\n if (nextEvent) {\n // Use setSelectedEvent() to ensure consistent behavior\n // Theme will be applied by useEventTheme() hook\n this.setSelectedEvent(nextEvent);\n }\n }\n\n // Lifecycle methods\n async initialize(): Promise<void> {\n // Only call super.initialize() which will call doInitialize() and fetchEvents()\n // Don't call fetchEvents() again here to avoid double-fetching\n await super.initialize();\n }\n\n cleanup(): void {\n super.cleanup();\n }\n\n protected async doInitialize(): Promise<void> {\n // Skip if already initialized\n if (this.isInitializedRef) {\n return;\n }\n \n // Skip if already fetching\n if (this.isFetchingRef) {\n return;\n }\n \n // Clean up old storage keys (migration from old key format)\n try {\n sessionStorage.removeItem('pace-core-selected-event');\n localStorage.removeItem('pace-core-selected-event');\n localStorage.removeItem('_sec_pace-core-selected-event');\n } catch (error) {\n logger.warn('EventService', 'Failed to clean up old storage keys:', error);\n }\n \n // Skip if no user\n // For event-required apps, selectedOrganisation may be null (org derived from event)\n // For org-required apps, selectedOrganisation is required\n if (!this.user) {\n return;\n }\n \n // Initial setup - fetch events on initialization\n await this.fetchEvents(false);\n \n // Mark as initialized after successful fetch\n this.isInitializedRef = true;\n }\n\n protected doCleanup(): void {\n // Cleanup any resources\n }\n\n private async fetchEvents(skipLoadPersisted: boolean = false): Promise<void> {\n // For event-required apps, selectedOrganisation may be null (org derived from event)\n // For org-required apps, selectedOrganisation is required\n if (!this.user || !this.session || !this.supabaseClient || !this.appName) {\n // Already false from initialization, just notify\n this.notify();\n return;\n }\n \n // Only set loading to true if we actually have dependencies and are going to fetch\n this._isLoading = true;\n this.notify();\n\n // Prevent multiple simultaneous fetches\n if (this.isFetchingRef) {\n return;\n }\n\n this.isFetchingRef = true;\n let isMounted = true;\n\n try {\n // Load app config if not already cached (only once per app)\n if (!this.appConfig && this.appName) {\n try {\n this.appConfig = await getAppConfigByName(this.appName);\n } catch (configError) {\n logger.warn('EventService', 'Failed to load app config, defaulting to event-required', {\n error: configError\n });\n // Default to event-required for safety\n this.appConfig = { requires_event: true };\n }\n }\n\n // Determine organisationId for RPC call\n // For event-required apps: org is derived from selectedEvent (if available), or null to get all accessible events\n // For org-required apps: org comes from selectedOrganisation\n // Super admins: pass null to see all events\n let organisationIdForRpc: string | null = null;\n \n // Check if user is super admin first\n let userIsSuperAdmin = false;\n try {\n userIsSuperAdmin = await isSuperAdmin(this.user.id as UUID);\n if (userIsSuperAdmin) {\n // Super admin: Pass null to see all events across all organisations\n organisationIdForRpc = null;\n } else {\n // Not super admin: determine org from context based on app type\n if (this.selectedEvent) {\n // If event is already selected, use its organisation\n organisationIdForRpc = this.selectedEvent.organisation_id;\n } else if (this.appConfig?.requires_event === true) {\n // Event-required app with no selected event yet: pass null to get all accessible events\n // The RPC will filter by event app roles, returning all events the user has access to\n organisationIdForRpc = null;\n } else if (this.selectedOrganisation) {\n // Org-required app: use selected organisation\n organisationIdForRpc = this.selectedOrganisation.id;\n } else {\n // No context available - this shouldn't happen for authenticated users\n logger.warn('EventService', 'No organisation context available for event fetch', {\n hasSelectedEvent: !!this.selectedEvent,\n hasSelectedOrganisation: !!this.selectedOrganisation,\n appRequiresEvent: this.appConfig?.requires_event\n });\n organisationIdForRpc = null; // Will return empty list\n }\n }\n } catch (superAdminCheckError) {\n // If super admin check fails, fall back to organisation-scoped query\n logger.warn('EventService', 'Failed to check super admin status, using organisation-scoped query', {\n error: superAdminCheckError\n });\n // Fallback: use available context\n if (this.selectedEvent) {\n organisationIdForRpc = this.selectedEvent.organisation_id;\n } else if (this.appConfig?.requires_event === true) {\n // Event-required app: pass null to get all accessible events\n organisationIdForRpc = null;\n } else if (this.selectedOrganisation) {\n organisationIdForRpc = this.selectedOrganisation.id;\n }\n }\n \n // Call the RPC function following the established pattern\n // For super admins, pass null for p_organisation_id to see all events\n let { data, error: rpcError } = await this.supabaseClient.rpc('data_user_events_get', {\n p_user_id: this.user.id,\n p_organisation_id: organisationIdForRpc,\n p_app_name: this.appName\n });\n \n if (rpcError) {\n logger.error('EventService', 'RPC error fetching events:', rpcError);\n throw new Error(rpcError.message || 'Failed to fetch events');\n }\n\n if (isMounted) {\n const eventsData = data || [];\n\n // Transform the data to match our Event interface\n // Type for RPC response from data_user_events_get\n interface EventRpcResponse {\n event_id: string;\n event_name: string;\n event_code: string;\n event_date: string | null;\n event_venue: string | null;\n event_participants: number | null;\n event_colours: Record<string, unknown> | null;\n organisation_id: string;\n is_visible: boolean;\n }\n \n const transformedEvents: Event[] = eventsData.map((event: EventRpcResponse) => ({\n id: event.event_id,\n event_id: event.event_id,\n event_name: event.event_name,\n event_code: event.event_code,\n event_date: event.event_date,\n event_venue: event.event_venue,\n event_participants: event.event_participants,\n event_colours: event.event_colours,\n event_logo: '', // No logo field in event table\n organisation_id: assertOrganisationId(event.organisation_id),\n is_visible: event.is_visible,\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString()\n }));\n\n this.events = transformedEvents;\n this.error = null;\n\n // Reset auto-selection ref for new events\n this.hasAutoSelectedRef = false;\n\n // Try to restore persisted event first (only if not skipping)\n if (!skipLoadPersisted) {\n const persistedEventLoaded = await this.loadPersistedEvent(transformedEvents);\n \n // If no persisted event was loaded and user hasn't explicitly cleared an event, auto-select the next event\n if (!persistedEventLoaded && !this.userClearedEventRef) {\n const nextEvent = this.getNextEventByDate(transformedEvents);\n if (nextEvent) {\n this.hasAutoSelectedRef = true;\n // Use setSelectedEvent() to ensure consistent behavior\n // Theme will be applied by useEventTheme() hook\n this.setSelectedEvent(nextEvent);\n }\n }\n } else {\n // If skipping persisted event load, still do auto-selection for new users\n if (!this.userClearedEventRef) {\n const nextEvent = this.getNextEventByDate(transformedEvents);\n if (nextEvent) {\n this.hasAutoSelectedRef = true;\n // Use setSelectedEvent() to ensure consistent behavior\n // Theme will be applied by useEventTheme() hook\n this.setSelectedEvent(nextEvent);\n }\n }\n }\n }\n } catch (err) {\n logger.error('EventService', 'Error fetching events:', err);\n const _error = err instanceof Error ? err : new Error('Unknown error occurred');\n \n if (isMounted) {\n this.error = _error;\n this.events = [];\n }\n } finally {\n if (isMounted) {\n this._isLoading = false;\n }\n this.isFetchingRef = false;\n this.notify();\n }\n }\n\n\n getNextEventByDate(events?: Event[]): Event | null {\n const eventsToUse = events || this.events;\n if (!eventsToUse || eventsToUse.length === 0) {\n return null;\n }\n\n // Get start of today (midnight) to compare dates only (ignore time)\n const now = new Date();\n const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();\n \n const futureEvents = eventsToUse.filter(event => {\n if (!event.event_date) return false;\n const eventDate = new Date(event.event_date);\n // Compare by date only (start of day), not by time\n const startOfEventDate = new Date(eventDate.getFullYear(), eventDate.getMonth(), eventDate.getDate()).getTime();\n return startOfEventDate >= startOfToday;\n });\n\n if (futureEvents.length > 0) {\n // Sort by date (ascending) to get the next event\n const sortedFutureEvents = futureEvents.sort((a, b) => {\n const dateA = new Date(a.event_date!);\n const dateB = new Date(b.event_date!);\n return dateA.getTime() - dateB.getTime();\n });\n\n return sortedFutureEvents[0];\n }\n\n // Fallback: If no future events found, return the most recent past event\n // This handles cases where users only have access to past events\n const pastEvents = eventsToUse.filter(event => {\n if (!event.event_date) return false;\n const eventDate = new Date(event.event_date);\n const startOfEventDate = new Date(eventDate.getFullYear(), eventDate.getMonth(), eventDate.getDate()).getTime();\n return startOfEventDate < startOfToday;\n });\n\n if (pastEvents.length > 0) {\n // Sort by date (descending) to get the most recent past event\n const sortedPastEvents = pastEvents.sort((a, b) => {\n const dateA = new Date(a.event_date!);\n const dateB = new Date(b.event_date!);\n return dateB.getTime() - dateA.getTime(); // Descending order\n });\n\n return sortedPastEvents[0];\n }\n\n // No events found at all\n return null;\n }\n}","/**\n * @file Inactivity Service Provider\n * @package @jmruthers/pace-core\n * @module Providers/Services\n * @since 0.1.0\n *\n * React provider for InactivityService.\n * Provides inactivity service instance to React components.\n */\n\nimport React, { createContext, useMemo, useEffect, useRef } from 'react';\nimport { type SupabaseClient, type User, type Session } from '@supabase/supabase-js';\nimport { InactivityService } from '../../services/InactivityService';\nimport { logger } from '../../utils/core/logger';\n\n// Context type\nexport interface InactivityServiceContextType {\n inactivityService: InactivityService;\n}\n\nexport const InactivityServiceContext = createContext<InactivityServiceContextType | null>(null);\n\nexport interface InactivityServiceProviderProps {\n children: React.ReactNode;\n supabaseClient: SupabaseClient;\n user: User | null;\n session: Session | null;\n idleTimeoutMs: number; // REQUIRED: No default - must be explicitly provided\n warnBeforeMs: number; // REQUIRED: No default - must be explicitly provided\n onIdleLogout: (reason: 'inactivity') => void;\n}\n\nexport function InactivityServiceProvider({ \n children, \n supabaseClient, \n user, \n session, \n idleTimeoutMs, // REQUIRED: No default - must be explicitly provided\n warnBeforeMs, // REQUIRED: No default - must be explicitly provided\n onIdleLogout\n}: InactivityServiceProviderProps) {\n // Create service instance once with useRef to avoid recreation on dependency changes\n const inactivityServiceRef = useRef<InactivityService | null>(null);\n \n if (!inactivityServiceRef.current) {\n inactivityServiceRef.current = new InactivityService(supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout);\n }\n \n const inactivityService = inactivityServiceRef.current;\n\n // Update service dependencies and initialize when dependencies change\n useEffect(() => {\n inactivityService.updateDependencies(supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout);\n \n // Re-initialize service when dependencies change\n let isMounted = true;\n \n inactivityService.initialize().catch(error => {\n if (isMounted) {\n logger.error('InactivityServiceProvider', 'Failed to initialize inactivity service:', error);\n }\n });\n\n return () => {\n isMounted = false;\n };\n }, [inactivityService, supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout]);\n\n // Cleanup service on unmount only\n useEffect(() => {\n return () => {\n inactivityService.cleanup();\n };\n }, [inactivityService]);\n\n const contextValue = useMemo(() => ({\n inactivityService\n }), [inactivityService]);\n\n return (\n <InactivityServiceContext.Provider value={contextValue}>\n {children}\n </InactivityServiceContext.Provider>\n );\n}","/**\n * @file Inactivity Service\n * @package @jmruthers/pace-core\n * @module Services\n * @since 0.1.0\n *\n * Inactivity service implementation.\n * Handles inactivity tracking, auto-logout, and warning modals.\n */\n\nimport { type SupabaseClient, type User, type Session } from '@supabase/supabase-js';\nimport { BaseService } from './base/BaseService';\nimport { IInactivityService } from './interfaces/IInactivityService';\nimport { logger } from '../utils/core/logger';\n\ninterface InactivityTracker {\n isIdle: boolean;\n timeRemaining: number;\n showWarning: boolean;\n isTracking: boolean;\n resetActivity: () => void;\n startTracking: () => void;\n stopTracking: () => void;\n cleanup?: () => void;\n}\n\nexport class InactivityService extends BaseService implements IInactivityService {\n private _showInactivityWarning = false;\n private _inactivityTimeRemaining = 0;\n private _isIdle = false;\n private _timeRemaining = 0;\n private _showWarning = false;\n private _isTracking = false;\n\n // Dependencies\n private supabaseClient: SupabaseClient | null = null;\n private user: User | null = null;\n private session: Session | null = null;\n private idleTimeoutMs = 30 * 60 * 1000; // 30 minutes\n private warnBeforeMs = 60 * 1000; // 60 seconds\n private onIdleLogout: ((reason: 'inactivity') => void) | null = null;\n\n // Internal state management\n private inactivityTracker: InactivityTracker | null = null;\n private isInactivityEnabled = true;\n private cleanupHandlers: (() => void) | null = null;\n\n constructor(\n supabaseClient: SupabaseClient | null,\n user: User | null,\n session: Session | null,\n idleTimeoutMs: number = 30 * 60 * 1000,\n warnBeforeMs: number = 60 * 1000,\n onIdleLogout: (reason: 'inactivity') => void\n ) {\n super();\n this.supabaseClient = supabaseClient;\n this.user = user;\n this.session = session;\n this.idleTimeoutMs = idleTimeoutMs;\n this.warnBeforeMs = warnBeforeMs;\n this.onIdleLogout = onIdleLogout;\n \n // Initialize time remaining to idle timeout\n this._timeRemaining = idleTimeoutMs;\n }\n\n // Interface implementation\n isIdle(): boolean { return this._isIdle; }\n getTimeRemaining(): number { return this._timeRemaining; }\n isWarningShown(): boolean { return this._showWarning; }\n isTracking(): boolean { return this._isTracking; }\n getShowInactivityWarning(): boolean { return this._showInactivityWarning; }\n getInactivityTimeRemaining(): number { return this._inactivityTimeRemaining; }\n\n // Additional getter methods that tests expect\n getIsIdle(): boolean { return this._isIdle; }\n getIsTracking(): boolean { return this._isTracking; }\n getShowWarning(): boolean { return this._showWarning; }\n\n // Additional methods for testing\n setShowInactivityWarning(value: boolean): void {\n this._showInactivityWarning = value;\n this.notify();\n }\n\n setInactivityTimeRemaining(value: number): void {\n this._inactivityTimeRemaining = value;\n this.notify();\n }\n\n setIsIdle(value: boolean): void {\n this._isIdle = value;\n this.notify();\n }\n\n setTimeRemaining(value: number): void {\n this._timeRemaining = value;\n this.notify();\n }\n\n setShowWarning(value: boolean): void {\n this._showWarning = value;\n this.notify();\n }\n\n setIsTracking(value: boolean): void {\n this._isTracking = value;\n this.notify();\n }\n\n triggerWarning(timeRemaining: number): void {\n this._showInactivityWarning = true;\n this._inactivityTimeRemaining = Math.ceil(timeRemaining / 1000);\n this._showWarning = true;\n this.notify();\n }\n\n triggerIdle(): void {\n this._isIdle = true;\n this.handleIdleLogout();\n this.notify();\n }\n\n // Update dependencies\n updateDependencies(\n supabaseClient: SupabaseClient | null,\n user: User | null,\n session: Session | null,\n idleTimeoutMs?: number,\n warnBeforeMs?: number,\n onIdleLogout?: (reason: 'inactivity') => void\n ): void {\n this.supabaseClient = supabaseClient;\n this.user = user;\n this.session = session;\n if (idleTimeoutMs !== undefined) this.idleTimeoutMs = idleTimeoutMs;\n if (warnBeforeMs !== undefined) this.warnBeforeMs = warnBeforeMs;\n if (onIdleLogout !== undefined) this.onIdleLogout = onIdleLogout;\n this.notify();\n }\n\n // Inactivity methods\n resetActivity(): void {\n if (this.inactivityTracker) {\n this.inactivityTracker.resetActivity();\n }\n \n // Store previous values\n const prevIsIdle = this._isIdle;\n const prevShowWarning = this._showWarning;\n const prevShowInactivityWarning = this._showInactivityWarning;\n const prevInactivityTimeRemaining = this._inactivityTimeRemaining;\n const prevTimeRemaining = this._timeRemaining;\n \n // Update state\n this._isIdle = false;\n this._showWarning = false;\n this._showInactivityWarning = false;\n this._inactivityTimeRemaining = 0;\n this._timeRemaining = this.idleTimeoutMs;\n \n // Only notify if values actually changed\n if (\n prevIsIdle !== this._isIdle ||\n prevShowWarning !== this._showWarning ||\n prevShowInactivityWarning !== this._showInactivityWarning ||\n prevInactivityTimeRemaining !== this._inactivityTimeRemaining ||\n prevTimeRemaining !== this._timeRemaining\n ) {\n this.notify();\n }\n }\n\n startTracking(): void {\n if (this.inactivityTracker) {\n this.inactivityTracker.startTracking();\n }\n this._isTracking = true;\n this.notify();\n }\n\n stopTracking(): void {\n if (this.inactivityTracker) {\n this.inactivityTracker.stopTracking();\n }\n this._isTracking = false;\n this.notify();\n }\n\n async handleIdleLogout(): Promise<void> {\n // Hide warning\n this._showInactivityWarning = false;\n this._inactivityTimeRemaining = 0;\n \n // Stop tracking\n this.stopTracking();\n \n // Sign out via Supabase\n try {\n if (this.supabaseClient) {\n await this.supabaseClient.auth.signOut();\n }\n } catch (error) {\n logger.error('InactivityService', 'Error during idle logout:', error);\n }\n \n // Call app callback for navigation/redirect\n this.onIdleLogout?.('inactivity');\n this.notify();\n }\n\n handleStaySignedIn(): void {\n this._showInactivityWarning = false;\n this._inactivityTimeRemaining = 0;\n this.resetActivity();\n this.notify();\n }\n\n async handleSignOutNow(): Promise<void> {\n this._showInactivityWarning = false;\n this._inactivityTimeRemaining = 0;\n this.stopTracking();\n \n // Sign out via Supabase\n try {\n if (this.supabaseClient) {\n await this.supabaseClient.auth.signOut();\n }\n } catch (error) {\n logger.error('InactivityService', 'Error during manual sign out:', error);\n }\n \n // Call app callback for navigation/redirect\n this.onIdleLogout?.('inactivity');\n this.notify();\n }\n\n // Lifecycle methods\n async initialize(): Promise<void> {\n await super.initialize();\n await this.setupInactivityTracker();\n }\n\n cleanup(): void {\n if (this.cleanupHandlers) {\n this.cleanupHandlers();\n this.cleanupHandlers = null;\n }\n if (this.inactivityTracker) {\n this.inactivityTracker = null;\n }\n this._isTracking = false;\n this._isIdle = false;\n this._showWarning = false;\n this._showInactivityWarning = false;\n this._timeRemaining = 0;\n this._inactivityTimeRemaining = 0;\n super.cleanup();\n }\n\n protected async doInitialize(): Promise<void> {\n // Production safety check for inactivity feature\n if (typeof window !== 'undefined') {\n const isProduction = import.meta.env.MODE === 'production';\n \n if (isProduction) {\n logger.warn('InactivityService', 'Inactivity feature enabled in production');\n }\n }\n }\n\n protected doCleanup(): void {\n // Cleanup any resources\n }\n\n private async setupInactivityTracker(): Promise<void> {\n if (typeof window === 'undefined') return;\n\n // Check if inactivity is enabled\n this.isInactivityEnabled = !!(this.user && this.session);\n\n if (!this.isInactivityEnabled) {\n return;\n }\n\n // Set up event handlers directly (no need to import hooks - services are pure TypeScript)\n this.setupEventHandlers();\n }\n\n private setupEventHandlers(): void {\n if (typeof window === 'undefined') return;\n\n let idleTimer: NodeJS.Timeout | null = null;\n let warningTimer: NodeJS.Timeout | null = null;\n let lastActivity = Date.now();\n let pollInterval: NodeJS.Timeout | null = null;\n \n // Store previous state for comparison\n let prevIsIdle = false;\n let prevShowWarning = false;\n let prevShowInactivityWarning = false;\n let prevInactivityTimeRemaining = 0;\n let prevTimeRemaining = this.idleTimeoutMs;\n\n // Poll every 10 seconds to check for state changes\n const pollInactivityState = () => {\n const now = Date.now();\n const timeSinceActivity = now - lastActivity;\n const timeUntilIdle = this.idleTimeoutMs - timeSinceActivity;\n const timeUntilWarning = (this.idleTimeoutMs - this.warnBeforeMs) - timeSinceActivity;\n \n // Calculate new state values based on time since last activity\n const newIsIdle = timeSinceActivity >= (this.idleTimeoutMs - this.warnBeforeMs);\n const newShowWarning = newIsIdle && timeSinceActivity < this.idleTimeoutMs;\n const newShowInactivityWarning = newShowWarning;\n const newInactivityTimeRemaining = newShowWarning \n ? Math.ceil((this.idleTimeoutMs - timeSinceActivity) / 1000)\n : 0;\n const newTimeRemaining = Math.max(0, timeUntilIdle);\n \n // Check if state actually changed\n const stateChanged = \n prevIsIdle !== newIsIdle ||\n prevShowWarning !== newShowWarning ||\n prevShowInactivityWarning !== newShowInactivityWarning ||\n prevInactivityTimeRemaining !== newInactivityTimeRemaining ||\n prevTimeRemaining !== newTimeRemaining;\n \n // Only update and notify if state changed\n if (stateChanged) {\n this._isIdle = newIsIdle;\n this._showWarning = newShowWarning;\n this._showInactivityWarning = newShowInactivityWarning;\n this._inactivityTimeRemaining = newInactivityTimeRemaining;\n this._timeRemaining = newTimeRemaining;\n \n // Update previous state\n prevIsIdle = newIsIdle;\n prevShowWarning = newShowWarning;\n prevShowInactivityWarning = newShowInactivityWarning;\n prevInactivityTimeRemaining = newInactivityTimeRemaining;\n prevTimeRemaining = newTimeRemaining;\n \n this.notify();\n \n // Handle idle logout if needed\n if (newIsIdle && timeSinceActivity >= this.idleTimeoutMs) {\n this.handleIdleLogout();\n }\n }\n \n // Update timers based on current state\n if (idleTimer) {\n clearTimeout(idleTimer);\n idleTimer = null;\n }\n \n if (warningTimer) {\n clearTimeout(warningTimer);\n warningTimer = null;\n }\n \n // Set up timers for next state transitions\n if (!newIsIdle && timeUntilIdle > 0) {\n idleTimer = setTimeout(() => {\n pollInactivityState();\n }, Math.min(10000, timeUntilIdle));\n }\n \n if (newShowWarning && timeUntilIdle > 0) {\n warningTimer = setTimeout(() => {\n this.handleIdleLogout();\n }, timeUntilIdle);\n }\n };\n\n const resetTimers = () => {\n // Only update lastActivity - don't notify yet\n lastActivity = Date.now();\n \n // Clear timers\n if (idleTimer) {\n clearTimeout(idleTimer);\n idleTimer = null;\n }\n \n if (warningTimer) {\n clearTimeout(warningTimer);\n warningTimer = null;\n }\n \n // Reset state values (will be checked on next poll)\n this._showInactivityWarning = false;\n this._inactivityTimeRemaining = 0;\n this._isIdle = false;\n this._showWarning = false;\n \n // Update previous state to match\n prevIsIdle = false;\n prevShowWarning = false;\n prevShowInactivityWarning = false;\n prevInactivityTimeRemaining = 0;\n prevTimeRemaining = this.idleTimeoutMs;\n \n // Poll will check and notify if needed\n };\n\n // Activity detection - only updates lastActivity, doesn't notify\n const activityEvents = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];\n \n const handleActivity = () => {\n resetTimers();\n // Don't call notify - polling will handle state updates\n };\n\n // Add event listeners\n activityEvents.forEach(event => {\n document.addEventListener(event, handleActivity, true);\n });\n\n // Start polling every 10 seconds\n pollInterval = setInterval(() => {\n pollInactivityState();\n }, 10000); // 10 seconds\n\n // Initial poll\n pollInactivityState();\n\n // Store cleanup function\n this.cleanupHandlers = () => {\n if (idleTimer) {\n clearTimeout(idleTimer);\n idleTimer = null;\n }\n \n if (warningTimer) {\n clearTimeout(warningTimer);\n warningTimer = null;\n }\n \n if (pollInterval) {\n clearInterval(pollInterval);\n pollInterval = null;\n }\n\n activityEvents.forEach(event => {\n document.removeEventListener(event, handleActivity, true);\n });\n\n this._isTracking = false;\n this._isIdle = false;\n this._showWarning = false;\n this._timeRemaining = 0;\n this._showInactivityWarning = false;\n this._inactivityTimeRemaining = 0;\n };\n\n this._isTracking = true;\n this.notify(); // Initial notification only\n }\n}","/**\n * @file Auth Service Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Services\n * @since 0.1.0\n *\n * React hook for AuthService.\n * Provides authentication service with reactive state updates.\n */\n\nimport { useContext, useReducer, useEffect, useRef } from 'react';\nimport { AuthServiceContext } from '../../providers/services/AuthServiceProvider';\nimport { AuthService } from '../../services/AuthService';\n\nexport function useAuthService(): AuthService {\n const context = useContext(AuthServiceContext);\n \n if (!context) {\n throw new Error('useAuthService must be used within AuthServiceProvider');\n }\n \n // Subscribe to service state changes with debouncing to prevent excessive re-renders\n const [, forceUpdate] = useReducer(x => x + 1, 0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n \n useEffect(() => {\n const debouncedUpdate = () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n timeoutRef.current = setTimeout(() => {\n forceUpdate();\n timeoutRef.current = null;\n }, 50); // 50ms debounce to batch rapid updates\n };\n \n const unsubscribe = context.authService.subscribe(debouncedUpdate);\n \n return () => {\n unsubscribe();\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [context.authService]);\n \n return context.authService;\n}","/**\n * @file Organisation Service Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Services\n * @since 0.1.0\n *\n * React hook for OrganisationService.\n * Provides organisation service with reactive state updates.\n */\n\nimport { useContext, useReducer, useEffect, useRef } from 'react';\nimport { OrganisationServiceContext } from '../../providers/services/OrganisationServiceProvider';\nimport { OrganisationService } from '../../services/OrganisationService';\n\nexport function useOrganisationService(): OrganisationService {\n const context = useContext(OrganisationServiceContext);\n \n if (!context) {\n throw new Error('useOrganisationService must be used within OrganisationServiceProvider');\n }\n \n // Subscribe to service state changes with debouncing to prevent excessive re-renders\n const [, forceUpdate] = useReducer(x => x + 1, 0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n \n useEffect(() => {\n const debouncedUpdate = () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n timeoutRef.current = setTimeout(() => {\n forceUpdate();\n timeoutRef.current = null;\n }, 50); // 50ms debounce to batch rapid updates\n };\n \n const unsubscribe = context.organisationService.subscribe(debouncedUpdate);\n \n return () => {\n unsubscribe();\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [context.organisationService]);\n \n return context.organisationService;\n}","/**\n * @file Organisation Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.1.0\n *\n * Convenience hook for accessing organisation context.\n * This hook provides a simple interface for working with organisations.\n * \n * Note: This is a convenience wrapper around the OrganisationService.\n * For better performance, consider using useOrganisationService directly.\n */\n\nimport { useOrganisationService } from './services/useOrganisationService';\nimport { Organisation, OrganisationMembership, OrganisationContextType } from '../types/organisation';\n\n/**\n * Hook to access organisation context\n * \n * @returns Organisation context with selected organisation, organisations list, and helper methods\n * @throws {Error} If used outside OrganisationServiceProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { selectedOrganisation, organisations, switchOrganisation } = useOrganisations();\n * \n * return (\n * <div>\n * <h1>Current Organisation: {selectedOrganisation.display_name}</h1>\n * {organisations.map(org => (\n * <button key={org.id} onClick={() => switchOrganisation(org.id)}>\n * {org.display_name}\n * </button>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useOrganisations(): OrganisationContextType {\n const organisationService = useOrganisationService();\n\n const selectedOrg = organisationService.getSelectedOrganisation();\n \n return {\n selectedOrganisation: selectedOrg as Organisation | null,\n organisations: organisationService.getOrganisations(),\n userMemberships: organisationService.getUserMemberships(),\n isLoading: organisationService.isLoading(),\n error: organisationService.getError(),\n hasValidOrganisationContext: organisationService.hasValidOrganisationContext(),\n isContextReady: organisationService.isContextReady(),\n setSelectedOrganisation: (org: Organisation | null) => organisationService.setSelectedOrganisation(org),\n switchOrganisation: (orgId: string) => organisationService.switchOrganisation(orgId),\n getUserRole: (orgId?: string) => organisationService.getUserRole(orgId),\n validateOrganisationAccess: (orgId: string) => organisationService.validateOrganisationAccess(orgId),\n refreshOrganisations: () => organisationService.refreshOrganisations(),\n ensureOrganisationContext: () => organisationService.ensureOrganisationContext(),\n isOrganisationSecure: () => organisationService.isOrganisationSecure(),\n getPrimaryOrganisation: () => organisationService.getPrimaryOrganisation(),\n };\n}\n\n","/**\n * @file Event Service Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Services\n * @since 0.1.0\n *\n * React hook for EventService.\n * Provides event service with reactive state updates.\n */\n\nimport { useContext, useReducer, useEffect, useRef } from 'react';\nimport { EventServiceContext } from '../../providers/services/EventServiceProvider';\nimport { EventService } from '../../services/EventService';\n\nexport function useEventService(): EventService {\n const context = useContext(EventServiceContext);\n \n if (!context) {\n throw new Error('useEventService must be used within EventServiceProvider');\n }\n \n // Subscribe to service state changes with debouncing to prevent excessive re-renders\n const [, forceUpdate] = useReducer(x => x + 1, 0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n \n useEffect(() => {\n const debouncedUpdate = () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n timeoutRef.current = setTimeout(() => {\n forceUpdate();\n timeoutRef.current = null;\n }, 50); // 50ms debounce to batch rapid updates\n };\n \n const unsubscribe = context.eventService.subscribe(debouncedUpdate);\n \n return () => {\n unsubscribe();\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [context.eventService]);\n \n return context.eventService;\n}","/**\n * @file Inactivity Service Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Services\n * @since 0.1.0\n *\n * React hook for InactivityService.\n * Provides inactivity service with reactive state updates.\n */\n\nimport { useContext, useReducer, useEffect, useRef } from 'react';\nimport { InactivityServiceContext } from '../../providers/services/InactivityServiceProvider';\nimport { InactivityService } from '../../services/InactivityService';\n\nexport function useInactivityService(): InactivityService {\n const context = useContext(InactivityServiceContext);\n \n if (!context) {\n throw new Error('useInactivityService must be used within InactivityServiceProvider');\n }\n \n // Subscribe to service state changes with debouncing to prevent excessive re-renders\n const [, forceUpdate] = useReducer(x => x + 1, 0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n \n useEffect(() => {\n const debouncedUpdate = () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n timeoutRef.current = setTimeout(() => {\n forceUpdate();\n timeoutRef.current = null;\n }, 50); // 50ms debounce to batch rapid updates\n };\n \n const unsubscribe = context.inactivityService.subscribe(debouncedUpdate);\n \n return () => {\n unsubscribe();\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n };\n }, [context.inactivityService]);\n \n return context.inactivityService;\n}","/**\n * @file useSessionRestoration Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.1.0\n *\n * Provides reactive session restoration state from the AuthService.\n * Handles timeout detection to prevent infinite loading loops when\n * Supabase session hydration from localStorage takes too long.\n */\n\nimport { useContext, useMemo, useEffect, useState } from 'react';\nimport { AuthServiceContext } from '../providers/services/AuthServiceProvider';\nimport type { SessionRestorationState } from '../types/auth';\nimport { createLogger } from '../utils/core/logger';\n\nconst log = createLogger('useSessionRestoration');\n\nconst SESSION_RESTORATION_TIMEOUT_MS = 5000;\n\nexport interface UseSessionRestorationResult extends SessionRestorationState {\n /** Indicates whether the restoration process exceeded the timeout window */\n hasTimedOut: boolean;\n /** Timeout duration in milliseconds */\n timeoutMs: number;\n}\n\nexport function useSessionRestoration(): UseSessionRestorationResult {\n const context = useContext(AuthServiceContext);\n\n if (!context) {\n throw new Error('useSessionRestoration must be used within AuthServiceProvider');\n }\n\n const { sessionRestoration } = context;\n const [hasTimedOut, setHasTimedOut] = useState(false);\n\n useEffect(() => {\n let timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n\n if (sessionRestoration.isRestoring && !sessionRestoration.restorationComplete && !sessionRestoration.restorationError) {\n setHasTimedOut(false);\n timeoutHandle = setTimeout(() => {\n log.warn('Session restoration timed out');\n setHasTimedOut(true);\n }, SESSION_RESTORATION_TIMEOUT_MS);\n } else {\n setHasTimedOut(false);\n }\n\n return () => {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n };\n }, [\n sessionRestoration.isRestoring,\n sessionRestoration.restorationComplete,\n sessionRestoration.restorationError\n ]);\n\n return useMemo(() => ({\n ...sessionRestoration,\n hasTimedOut,\n timeoutMs: SESSION_RESTORATION_TIMEOUT_MS,\n }), [sessionRestoration, hasTimedOut]);\n}\n"],"mappings":"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","names":["createContext","useContext","useMemo","useRef","useEffect","useState","useReducer","error","createContext","useMemo","useEffect","jsx","createContext","useEffect","useMemo","createContext","useMemo","useEffect","useRef","jsx","createContext","useRef","useEffect","useMemo","createContext","useMemo","useEffect","useRef","jsx","createContext","useRef","useEffect","useMemo","useEffect","useRef","useRef","useEffect","useContext","useReducer","useEffect","useRef","useContext","useReducer","useRef","useEffect","useContext","useReducer","useEffect","useRef","useContext","useReducer","useRef","useEffect","useContext","useReducer","useEffect","useRef","useContext","useReducer","useRef","useEffect","useContext","useMemo","useEffect","useState","useContext","useState","useEffect","useMemo","jsx","createContext","useContext","useMemo","useState","useRef","useEffect","getAppConfigByName","useReducer"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
2
|
+
useOrganisationSecurity,
|
|
3
|
+
useResolvedScope
|
|
4
|
+
} from "./chunk-3XTALGJF.js";
|
|
5
5
|
import {
|
|
6
6
|
EventServiceContext,
|
|
7
7
|
useUnifiedAuth
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-BYFSK72L.js";
|
|
9
9
|
import {
|
|
10
10
|
setOrganisationContext
|
|
11
11
|
} from "./chunk-VBXEHIUJ.js";
|
|
@@ -20,7 +20,8 @@ function useSecureDataAccess() {
|
|
|
20
20
|
const eventServiceContext = useContext(EventServiceContext);
|
|
21
21
|
const eventFromContext = eventServiceContext?.eventService?.getSelectedEvent() || null;
|
|
22
22
|
const effectiveSelectedEvent = selectedEvent || eventFromContext;
|
|
23
|
-
const {
|
|
23
|
+
const { superAdminContext } = useOrganisationSecurity();
|
|
24
|
+
const isSuperAdmin = superAdminContext.isSuperAdmin;
|
|
24
25
|
const { resolvedScope } = useResolvedScope({
|
|
25
26
|
supabase,
|
|
26
27
|
selectedOrganisationId: selectedOrganisation?.id || null,
|
|
@@ -71,9 +72,10 @@ function useSecureDataAccess() {
|
|
|
71
72
|
// SECURITY: Emergency additions for Phase 1 fixes
|
|
72
73
|
"cake_meal",
|
|
73
74
|
"cake_mealtype",
|
|
74
|
-
|
|
75
|
-
//
|
|
76
|
-
//
|
|
75
|
+
// NOTE: core_person, core_member, core_contact, core_consent, core_identification,
|
|
76
|
+
// core_qualification, and medi_profile are person-scoped tables that do NOT have
|
|
77
|
+
// organisation_id columns. They were removed as part of the person-scoped profiles migration.
|
|
78
|
+
// Do NOT add organisation_id filters to these tables - it will cause 400 errors.
|
|
77
79
|
// SECURITY: Phase 3A additions - medical and personal data
|
|
78
80
|
// NOTE: medi_condition, medi_diet, medi_action_plan, medi_profile_versions are now person-scoped
|
|
79
81
|
// (via medi_profile) - removed from this list
|
|
@@ -177,7 +179,45 @@ function useSecureDataAccess() {
|
|
|
177
179
|
"cake_meal",
|
|
178
180
|
"cake_mealtype",
|
|
179
181
|
"core_person",
|
|
180
|
-
|
|
182
|
+
// SECURITY: These tables still have organisation_id columns and must be filtered
|
|
183
|
+
"core_member",
|
|
184
|
+
"core_contact",
|
|
185
|
+
"core_consent",
|
|
186
|
+
"core_identification",
|
|
187
|
+
"core_qualification",
|
|
188
|
+
"medi_profile",
|
|
189
|
+
// SECURITY: Phase 3A additions - medical and personal data
|
|
190
|
+
"medi_condition",
|
|
191
|
+
"medi_diet",
|
|
192
|
+
"medi_action_plan",
|
|
193
|
+
"medi_profile_versions",
|
|
194
|
+
"core_identification_type",
|
|
195
|
+
"form_responses",
|
|
196
|
+
"form_response_values",
|
|
197
|
+
"forms",
|
|
198
|
+
// SECURITY: Phase 3B additions - remaining critical tables
|
|
199
|
+
"invoice",
|
|
200
|
+
"line_item",
|
|
201
|
+
"credit_balance",
|
|
202
|
+
"payment_method",
|
|
203
|
+
"form_contexts",
|
|
204
|
+
"form_field_config",
|
|
205
|
+
"form_fields",
|
|
206
|
+
"cake_delivery",
|
|
207
|
+
"cake_diettype",
|
|
208
|
+
"cake_diner",
|
|
209
|
+
"cake_dish",
|
|
210
|
+
"cake_item",
|
|
211
|
+
"cake_logistics",
|
|
212
|
+
"cake_mealplan",
|
|
213
|
+
"cake_package",
|
|
214
|
+
"cake_recipe",
|
|
215
|
+
"cake_supplier",
|
|
216
|
+
"cake_supply",
|
|
217
|
+
"cake_unit",
|
|
218
|
+
"event_app_access",
|
|
219
|
+
"base_application",
|
|
220
|
+
"base_questions"
|
|
181
221
|
];
|
|
182
222
|
if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {
|
|
183
223
|
query = query.eq("organisation_id", organisationId);
|
|
@@ -408,4 +448,4 @@ function useSecureDataAccess() {
|
|
|
408
448
|
export {
|
|
409
449
|
useSecureDataAccess
|
|
410
450
|
};
|
|
411
|
-
//# sourceMappingURL=chunk-
|
|
451
|
+
//# sourceMappingURL=chunk-EXUD6RNJ.js.map
|