@jmruthers/pace-core 0.6.2 → 0.6.3
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 +45 -0
- package/cursor-rules/00-pace-core-compliance.mdc +34 -2
- package/dist/{AuthService-BPvc3Ka0.d.ts → AuthService-Cb34EQs3.d.ts} +9 -1
- package/dist/{DataTable-TPTKCX4D.js → DataTable-THFPBKTP.js} +9 -8
- package/dist/{PublicPageProvider-DC6kCaqf.d.ts → PublicPageProvider-DEMpysFR.d.ts} +45 -67
- package/dist/{UnifiedAuthProvider-CVcTjx-d.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +1 -8
- package/dist/{UnifiedAuthProvider-CH6Z342H.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
- package/dist/{api-MVVQZLJI.js → api-IAGWF3ZG.js} +10 -10
- package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
- package/dist/{chunk-SFZUDBL5.js → chunk-2T2IG7T7.js} +70 -56
- package/dist/chunk-2T2IG7T7.js.map +1 -0
- package/dist/{chunk-MMZ7JXPU.js → chunk-6Z7LTB3D.js} +13 -21
- package/dist/{chunk-MMZ7JXPU.js.map → chunk-6Z7LTB3D.js.map} +1 -1
- package/dist/{chunk-6J4GEEJR.js → chunk-CNCQDFLN.js} +53 -27
- package/dist/chunk-CNCQDFLN.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-EHMR7VYL.js → chunk-DWUBLJJM.js} +361 -187
- package/dist/chunk-DWUBLJJM.js.map +1 -0
- package/dist/{chunk-2UOI2FG5.js → chunk-HFZBI76P.js} +4 -4
- package/dist/{chunk-F2IMUDXZ.js → chunk-M7MPQISP.js} +2 -2
- package/dist/{chunk-3XC4CPTD.js → chunk-PQBSKX33.js} +244 -5727
- package/dist/chunk-PQBSKX33.js.map +1 -0
- package/dist/chunk-QRPVRXYT.js +226 -0
- package/dist/chunk-QRPVRXYT.js.map +1 -0
- package/dist/{chunk-24UVZUZG.js → chunk-RWEBCB47.js} +129 -387
- package/dist/chunk-RWEBCB47.js.map +1 -0
- package/dist/{chunk-XWQCNGTQ.js → chunk-YDQHOZNA.js} +173 -79
- package/dist/chunk-YDQHOZNA.js.map +1 -0
- package/dist/{chunk-NECFR5MM.js → chunk-ZNIWI3UC.js} +562 -644
- package/dist/chunk-ZNIWI3UC.js.map +1 -0
- package/dist/components.d.ts +2 -2
- package/dist/components.js +12 -13
- package/dist/contextValidator-3JNZKUTX.js +9 -0
- package/dist/contextValidator-3JNZKUTX.js.map +1 -0
- package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +7 -6
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +21 -16
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +4 -3
- package/dist/rbac/index.d.ts +67 -27
- package/dist/rbac/index.js +15 -8
- package/dist/styles/index.js +1 -1
- package/dist/theming/runtime.js +1 -1
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-1oMokgLF.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +7 -16
- package/dist/utils.js +5 -7
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +14 -16
- package/docs/api/modules.md +3796 -2513
- package/docs/components/context-selector.md +126 -0
- package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
- package/docs/pace-mint-fix-auto-selection.md +218 -0
- package/docs/pace-mint-rbac-setup.md +391 -0
- package/docs/rbac/secure-client-protection.md +330 -0
- package/package.json +3 -3
- package/scripts/audit/core/checks/compliance.cjs +72 -0
- package/scripts/audit/core/checks/dependencies.cjs +559 -28
- package/scripts/audit/core/checks/documentation.cjs +68 -3
- package/scripts/audit/core/checks/environment.cjs +2 -14
- package/scripts/audit/core/checks/error-handling.cjs +47 -6
- package/src/components/ContextSelector/ContextSelector.tsx +384 -0
- package/src/components/ContextSelector/index.ts +3 -0
- package/src/components/DataTable/components/RowComponent.tsx +19 -19
- package/src/components/DataTable/components/UnifiedTableBody.tsx +2 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +8 -6
- package/src/components/Dialog/Dialog.tsx +29 -1
- package/src/components/FileDisplay/FileDisplay.tsx +42 -10
- package/src/components/Header/Header.test.tsx +43 -73
- package/src/components/Header/Header.tsx +44 -45
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +9 -9
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +135 -33
- package/src/components/PaceAppLayout/README.md +14 -17
- package/src/components/PaceAppLayout/test-setup.tsx +2 -2
- package/src/components/index.ts +5 -5
- package/src/eslint-rules/pace-core-compliance.cjs +106 -0
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
- package/src/hooks/useAppConfig.ts +15 -30
- package/src/hooks/useFileDisplay.ts +77 -50
- package/src/index.ts +4 -5
- package/src/providers/services/AuthServiceProvider.tsx +17 -7
- package/src/providers/services/EventServiceProvider.tsx +33 -5
- package/src/providers/services/UnifiedAuthProvider.tsx +90 -134
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
- package/src/rbac/adapters.tsx +2 -2
- package/src/rbac/api.test.ts +59 -51
- package/src/rbac/api.ts +178 -132
- package/src/rbac/components/PagePermissionGuard.tsx +38 -10
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
- package/src/rbac/hooks/permissions/useAccessLevel.ts +1 -1
- package/src/rbac/hooks/permissions/useCan.ts +41 -11
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +1 -1
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +1 -1
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +1 -1
- package/src/rbac/hooks/useCan.test.ts +0 -9
- package/src/rbac/hooks/useRBAC.test.ts +1 -5
- package/src/rbac/hooks/useRBAC.ts +36 -37
- package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
- package/src/rbac/hooks/useResolvedScope.ts +35 -40
- package/src/rbac/hooks/useSecureSupabase.ts +7 -7
- package/src/rbac/index.ts +7 -0
- package/src/rbac/secureClient.test.ts +22 -18
- package/src/rbac/secureClient.ts +103 -16
- package/src/rbac/security.ts +0 -17
- package/src/rbac/types.ts +1 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
- package/src/rbac/utils/clientSecurity.ts +93 -0
- package/src/rbac/utils/contextValidator.ts +77 -168
- package/src/services/AuthService.ts +39 -7
- package/src/services/EventService.ts +186 -54
- package/src/services/OrganisationService.ts +81 -14
- package/src/services/__tests__/EventService.test.ts +1 -2
- package/src/services/base/BaseService.ts +3 -0
- package/src/utils/dynamic/dynamicUtils.ts +7 -4
- package/dist/chunk-24UVZUZG.js.map +0 -1
- package/dist/chunk-3XC4CPTD.js.map +0 -1
- package/dist/chunk-6J4GEEJR.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js +0 -38
- package/dist/chunk-EHMR7VYL.js.map +0 -1
- package/dist/chunk-NECFR5MM.js.map +0 -1
- package/dist/chunk-SFZUDBL5.js.map +0 -1
- package/dist/chunk-XWQCNGTQ.js.map +0 -1
- package/docs/api/classes/ColumnFactory.md +0 -243
- package/docs/api/classes/InvalidScopeError.md +0 -73
- package/docs/api/classes/Logger.md +0 -178
- package/docs/api/classes/MissingUserContextError.md +0 -66
- package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
- package/docs/api/classes/PermissionDeniedError.md +0 -73
- package/docs/api/classes/RBACAuditManager.md +0 -297
- package/docs/api/classes/RBACCache.md +0 -322
- package/docs/api/classes/RBACEngine.md +0 -171
- package/docs/api/classes/RBACError.md +0 -76
- package/docs/api/classes/RBACNotInitializedError.md +0 -66
- package/docs/api/classes/SecureSupabaseClient.md +0 -163
- package/docs/api/classes/StorageUtils.md +0 -328
- package/docs/api/enums/FileCategory.md +0 -184
- package/docs/api/enums/LogLevel.md +0 -54
- package/docs/api/enums/RBACErrorCode.md +0 -228
- package/docs/api/enums/RPCFunction.md +0 -118
- package/docs/api/interfaces/AddressFieldProps.md +0 -241
- package/docs/api/interfaces/AddressFieldRef.md +0 -94
- package/docs/api/interfaces/AggregateConfig.md +0 -43
- package/docs/api/interfaces/AutocompleteOptions.md +0 -75
- package/docs/api/interfaces/AvatarProps.md +0 -128
- package/docs/api/interfaces/BadgeProps.md +0 -34
- package/docs/api/interfaces/ButtonProps.md +0 -56
- package/docs/api/interfaces/CalendarProps.md +0 -73
- package/docs/api/interfaces/CardProps.md +0 -69
- package/docs/api/interfaces/ColorPalette.md +0 -7
- package/docs/api/interfaces/ColorShade.md +0 -66
- package/docs/api/interfaces/ComplianceResult.md +0 -30
- package/docs/api/interfaces/DataAccessRecord.md +0 -96
- package/docs/api/interfaces/DataRecord.md +0 -11
- package/docs/api/interfaces/DataTableAction.md +0 -252
- package/docs/api/interfaces/DataTableColumn.md +0 -504
- package/docs/api/interfaces/DataTableProps.md +0 -625
- package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
- package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
- package/docs/api/interfaces/DatabaseIssue.md +0 -41
- package/docs/api/interfaces/EmptyStateConfig.md +0 -61
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
- package/docs/api/interfaces/ErrorBoundaryProps.md +0 -147
- package/docs/api/interfaces/ErrorBoundaryProviderProps.md +0 -36
- package/docs/api/interfaces/ErrorBoundaryState.md +0 -75
- package/docs/api/interfaces/EventAppRoleData.md +0 -71
- package/docs/api/interfaces/ExportColumn.md +0 -90
- package/docs/api/interfaces/ExportOptions.md +0 -126
- package/docs/api/interfaces/FileDisplayProps.md +0 -249
- package/docs/api/interfaces/FileMetadata.md +0 -129
- package/docs/api/interfaces/FileReference.md +0 -118
- package/docs/api/interfaces/FileSizeLimits.md +0 -7
- package/docs/api/interfaces/FileUploadOptions.md +0 -139
- package/docs/api/interfaces/FileUploadProps.md +0 -296
- package/docs/api/interfaces/FooterProps.md +0 -107
- package/docs/api/interfaces/FormFieldProps.md +0 -166
- package/docs/api/interfaces/FormProps.md +0 -113
- package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
- package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
- package/docs/api/interfaces/InputProps.md +0 -56
- package/docs/api/interfaces/LabelProps.md +0 -107
- package/docs/api/interfaces/LoggerConfig.md +0 -62
- package/docs/api/interfaces/LoginFormProps.md +0 -187
- package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
- package/docs/api/interfaces/NavigationContextType.md +0 -164
- package/docs/api/interfaces/NavigationGuardProps.md +0 -139
- package/docs/api/interfaces/NavigationItem.md +0 -120
- package/docs/api/interfaces/NavigationMenuProps.md +0 -221
- package/docs/api/interfaces/NavigationProviderProps.md +0 -117
- package/docs/api/interfaces/Organisation.md +0 -140
- package/docs/api/interfaces/OrganisationContextType.md +0 -388
- package/docs/api/interfaces/OrganisationMembership.md +0 -140
- package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
- package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
- package/docs/api/interfaces/PaceAppLayoutProps.md +0 -409
- package/docs/api/interfaces/PaceLoginPageProps.md +0 -49
- package/docs/api/interfaces/PageAccessRecord.md +0 -85
- package/docs/api/interfaces/PagePermissionContextType.md +0 -140
- package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
- package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
- package/docs/api/interfaces/PaletteData.md +0 -41
- package/docs/api/interfaces/ParsedAddress.md +0 -120
- package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
- package/docs/api/interfaces/ProgressProps.md +0 -42
- package/docs/api/interfaces/ProtectedRouteProps.md +0 -78
- package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
- package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
- package/docs/api/interfaces/PublicPageLayoutProps.md +0 -185
- package/docs/api/interfaces/QuickFix.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
- package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
- package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
- package/docs/api/interfaces/RBACConfig.md +0 -133
- package/docs/api/interfaces/RBACContext.md +0 -52
- package/docs/api/interfaces/RBACLogger.md +0 -112
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
- package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
- package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
- package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
- package/docs/api/interfaces/RBACResult.md +0 -58
- package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
- package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
- package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
- package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
- package/docs/api/interfaces/RBACRolesListParams.md +0 -52
- package/docs/api/interfaces/RBACRolesListResult.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
- package/docs/api/interfaces/ResourcePermissions.md +0 -155
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
- package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
- package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
- package/docs/api/interfaces/RoleManagementResult.md +0 -52
- package/docs/api/interfaces/RouteAccessRecord.md +0 -107
- package/docs/api/interfaces/RouteConfig.md +0 -134
- package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
- package/docs/api/interfaces/SecureDataContextType.md +0 -168
- package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
- package/docs/api/interfaces/SetupIssue.md +0 -41
- package/docs/api/interfaces/StorageConfig.md +0 -41
- package/docs/api/interfaces/StorageFileInfo.md +0 -74
- package/docs/api/interfaces/StorageFileMetadata.md +0 -151
- package/docs/api/interfaces/StorageListOptions.md +0 -99
- package/docs/api/interfaces/StorageListResult.md +0 -41
- package/docs/api/interfaces/StorageUploadOptions.md +0 -101
- package/docs/api/interfaces/StorageUploadResult.md +0 -63
- package/docs/api/interfaces/StorageUrlOptions.md +0 -60
- package/docs/api/interfaces/StyleImport.md +0 -19
- package/docs/api/interfaces/SwitchProps.md +0 -34
- package/docs/api/interfaces/TabsContentProps.md +0 -9
- package/docs/api/interfaces/TabsListProps.md +0 -9
- package/docs/api/interfaces/TabsProps.md +0 -9
- package/docs/api/interfaces/TabsTriggerProps.md +0 -50
- package/docs/api/interfaces/TextareaProps.md +0 -53
- package/docs/api/interfaces/ToastActionElement.md +0 -12
- package/docs/api/interfaces/ToastProps.md +0 -9
- package/docs/api/interfaces/UnifiedAuthContextType.md +0 -823
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -173
- package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
- package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -138
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -84
- package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
- package/docs/api/interfaces/UsePublicEventReturn.md +0 -71
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -123
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -97
- package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
- package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
- package/docs/api/interfaces/UserEventAccess.md +0 -121
- package/docs/api/interfaces/UserMenuProps.md +0 -88
- package/docs/api/interfaces/UserProfile.md +0 -63
- package/src/components/EventSelector/EventSelector.test.tsx +0 -720
- package/src/components/EventSelector/EventSelector.tsx +0 -423
- package/src/components/EventSelector/index.ts +0 -3
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -327
- package/src/components/OrganisationSelector/index.ts +0 -9
- /package/dist/{DataTable-TPTKCX4D.js.map → DataTable-THFPBKTP.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-CH6Z342H.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
- /package/dist/{api-MVVQZLJI.js.map → api-IAGWF3ZG.js.map} +0 -0
- /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
- /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/dist/{chunk-2UOI2FG5.js.map → chunk-HFZBI76P.js.map} +0 -0
- /package/dist/{chunk-F2IMUDXZ.js.map → chunk-M7MPQISP.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useEvents.ts","../src/components/ErrorBoundary/ErrorBoundaryContext.tsx","../src/components/ErrorBoundary/ErrorBoundary.tsx","../src/components/ErrorBoundary/index.ts","../src/components/PublicLayout/PublicPageProvider.tsx","../src/hooks/useAppConfig.ts","../src/hooks/useOrganisationSecurity.ts"],"sourcesContent":["/**\n * @file Event Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.1.0\n *\n * Convenience hook for accessing event context.\n * This hook provides a simple interface for working with events.\n * \n * Note: This is a convenience wrapper around the EventService.\n * For better performance, consider using useEventService directly.\n */\n\nimport { useMemo, useRef } from 'react';\nimport { useEventService } from './services/useEventService';\nimport { Event } from '../types/event';\n\nexport interface EventContextType {\n events: Event[];\n selectedEvent: Event | null;\n isLoading: boolean;\n error: Error | null;\n setSelectedEvent: (event: Event | null) => void;\n refreshEvents: () => Promise<void>;\n clearEventSelection: () => Promise<void>;\n}\n\n/**\n * Hook to access event context\n * \n * @returns Event context with events, selected event, and helper methods\n * @throws {Error} If used outside EventServiceProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { events, selectedEvent, setSelectedEvent } = useEvents();\n * \n * return (\n * <div>\n * {events.map(event => (\n * <button key={event.id} onClick={() => setSelectedEvent(event)}>\n * {event.event_name}\n * </button>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useEvents(): EventContextType {\n const eventService = useEventService();\n\n // Get current state - service subscription will trigger re-renders when state changes\n const rawEvents = eventService.getEvents();\n const selectedEvent = eventService.getSelectedEvent();\n const isLoading = eventService.isLoading();\n const error = eventService.getError();\n\n // Use refs to track previous values and only create new array reference when events actually change\n const prevEventsRef = useRef<Event[]>([]);\n const prevEventsIdsRef = useRef<string>('');\n \n // Compare event IDs to detect actual changes (more efficient than deep comparison)\n // Do this outside useMemo to avoid dependency on rawEvents array\n const currentEventsIds = rawEvents.map(e => e.event_id || e.id).join(',');\n const eventsChanged = currentEventsIds !== prevEventsIdsRef.current;\n \n // Only create new array reference when events actually changed\n // Update refs synchronously, then memoize based on the ID string\n if (eventsChanged) {\n prevEventsRef.current = rawEvents;\n prevEventsIdsRef.current = currentEventsIds;\n }\n \n // Return stable array reference - only changes when event IDs change\n const events = useMemo(() => {\n return prevEventsRef.current;\n }, [currentEventsIds]);\n\n // Memoize callbacks to prevent unnecessary re-renders in child components\n const setSelectedEventCallback = useMemo(\n () => (event: Event | null) => eventService.setSelectedEvent(event),\n [eventService]\n );\n const refreshEventsCallback = useMemo(\n () => () => eventService.refreshEvents(),\n [eventService]\n );\n const clearEventSelectionCallback = useMemo(\n () => () => eventService.clearEventSelection(),\n [eventService]\n );\n\n // Memoize the return object - only recreate when actual values change\n return useMemo(() => ({\n events,\n selectedEvent,\n isLoading,\n error,\n setSelectedEvent: setSelectedEventCallback,\n refreshEvents: refreshEventsCallback,\n clearEventSelection: clearEventSelectionCallback,\n }), [events, selectedEvent?.event_id, isLoading, error?.message, setSelectedEventCallback, refreshEventsCallback, clearEventSelectionCallback]);\n}\n\n","/**\n * @file Error Boundary Context\n * @package @jmruthers/pace-core\n * @module Components/ErrorBoundary\n * @since 0.6.2\n *\n * Context provider for global error handler configuration in ErrorBoundary components.\n * Allows applications to register a default error handler once, which is then used by\n * all ErrorBoundary instances unless overridden.\n *\n * @example\n * ```tsx\n * // Setup once in main.tsx\n * <ErrorBoundaryProvider\n * defaultErrorHandler={(error, errorInfo, errorId, componentName) => {\n * if (import.meta.env.DEV) {\n * console.error(`[${componentName}] ErrorBoundary caught error:`, error, errorInfo);\n * }\n * errorTracking.captureException(error, {\n * componentStack: errorInfo.componentStack,\n * errorBoundary: true,\n * errorId,\n * componentName,\n * });\n * }}\n * >\n * <App />\n * </ErrorBoundaryProvider>\n *\n * // Usage - no onError needed, uses global handler\n * <ErrorBoundary componentName=\"AppRoot\">\n * <AppContent />\n * </ErrorBoundary>\n * ```\n */\n\nimport React, { createContext, useContext, ReactNode } from 'react';\n\n/**\n * Type definition for the global error handler function\n * @public\n */\nexport type GlobalErrorHandler = (\n error: Error,\n errorInfo: React.ErrorInfo,\n errorId: string,\n componentName: string\n) => void;\n\n/**\n * Context value type for ErrorBoundary configuration\n * @internal\n */\ninterface ErrorBoundaryContextType {\n /** Global error handler that will be used by all ErrorBoundary instances */\n defaultErrorHandler?: GlobalErrorHandler;\n}\n\n/**\n * Context for ErrorBoundary global configuration\n * @internal\n */\nconst ErrorBoundaryContext = createContext<ErrorBoundaryContextType | undefined>(undefined);\n\n/**\n * Props for ErrorBoundaryProvider component\n * @public\n */\nexport interface ErrorBoundaryProviderProps {\n /** Children to wrap with the provider */\n children: ReactNode;\n /** Global error handler that will be used by all ErrorBoundary instances unless overridden */\n defaultErrorHandler?: GlobalErrorHandler;\n}\n\n/**\n * Provider component for ErrorBoundary global error handler configuration\n * \n * Wrap your application with this provider to set a default error handler that will\n * be used by all ErrorBoundary instances unless they provide their own onError prop.\n * \n * @example\n * ```tsx\n * <ErrorBoundaryProvider\n * defaultErrorHandler={(error, errorInfo, errorId, componentName) => {\n * errorTracking.captureException(error, {\n * componentStack: errorInfo.componentStack,\n * errorBoundary: true,\n * errorId,\n * componentName,\n * });\n * }}\n * >\n * <App />\n * </ErrorBoundaryProvider>\n * ```\n * \n * @public\n */\n/**\n * Error boundary provider component.\n * Provides global error handling configuration to child ErrorBoundary components.\n * \n * @param props - Error boundary provider configuration\n * @returns The error boundary provider\n */\nexport function ErrorBoundaryProvider({\n children,\n defaultErrorHandler,\n}: ErrorBoundaryProviderProps) {\n const contextValue: ErrorBoundaryContextType = {\n defaultErrorHandler,\n };\n\n return (\n <ErrorBoundaryContext.Provider value={contextValue}>\n {children}\n </ErrorBoundaryContext.Provider>\n );\n}\n\n/**\n * Hook to access ErrorBoundary context\n * @internal\n */\nexport function useErrorBoundaryContext(): ErrorBoundaryContextType | undefined {\n return useContext(ErrorBoundaryContext);\n}\n\n","/**\n * @file Error Boundary Component\n * @package @jmruthers/pace-core\n * @module Components/ErrorBoundary\n * @since 0.1.0\n *\n * A comprehensive error boundary component that catches JavaScript errors in child components\n * and provides fallback UI with retry functionality and error reporting.\n *\n * Features:\n * - Catches JavaScript errors in component tree\n * - Custom fallback UI with retry functionality\n * - Error reporting and logging\n * - Performance monitoring integration\n * - Retry mechanism with configurable attempts\n * - Development mode error details\n * - Accessibility compliant error display\n * - Component-specific error tracking\n * - Error ID generation for tracking\n *\n * @example\n * ```tsx\n * // Basic error boundary\n * <ErrorBoundary>\n * <MyComponent />\n * </ErrorBoundary>\n * \n * // Using global error handler (recommended for applications)\n * import { ErrorBoundaryProvider, ErrorBoundary } from '@jmruthers/pace-core';\n * \n * // Setup once in main.tsx\n * <ErrorBoundaryProvider\n * defaultErrorHandler={(error, errorInfo, errorId, componentName) => {\n * if (import.meta.env.DEV) {\n * console.error(`[${componentName}] ErrorBoundary caught error:`, error, errorInfo);\n * }\n * errorTracking.captureException(error, {\n * componentStack: errorInfo.componentStack,\n * errorBoundary: true,\n * errorId,\n * componentName,\n * });\n * }}\n * >\n * <App />\n * </ErrorBoundaryProvider>\n * \n * // Then use ErrorBoundary without onError prop - uses global handler\n * <ErrorBoundary componentName=\"AppRoot\">\n * <AppContent />\n * </ErrorBoundary>\n * \n * // Error boundary with custom fallback\n * <ErrorBoundary\n * componentName=\"UserProfile\"\n * fallback={<section>Something went wrong loading the profile.</section>}\n * onError={(error, errorInfo, errorId) => {\n * console.log('Error caught:', errorId, error);\n * }}\n * >\n * <UserProfile />\n * </ErrorBoundary>\n * \n * // Error boundary with retry functionality\n * <ErrorBoundary\n * componentName=\"DataTable\"\n * maxRetries={3}\n * enableRetry={true}\n * enableReporting={true}\n * onError={(error, errorInfo, errorId) => {\n * // Send to error reporting service\n * errorReportingService.report({ error, errorInfo, errorId });\n * }}\n * >\n * <DataTable data={data} />\n * </ErrorBoundary>\n * \n * // Wrapping multiple components\n * <ErrorBoundary componentName=\"App\">\n * <Header />\n * <ErrorBoundary componentName=\"MainContent\">\n * <MainContent />\n * </ErrorBoundary>\n * <Footer />\n * </ErrorBoundary>\n * \n * // Per-instance onError overrides global handler\n * <ErrorBoundary \n * componentName=\"SpecialCase\"\n * onError={(error, errorInfo, errorId) => {\n * // Custom handling for this specific case - global handler won't be called\n * }}\n * >\n * <SpecialComponent />\n * </ErrorBoundary>\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Proper ARIA role=\"alert\" for error announcements\n * - Screen reader friendly error messages\n * - Keyboard accessible retry buttons\n * - High contrast error styling\n * - Clear error identification\n *\n * @performance\n * - Efficient error state management\n * - Performance monitoring integration\n * - Minimal impact on normal rendering\n * - Configurable error reporting\n *\n * @dependencies\n * - React 19+ - Component lifecycle\n * - Performance monitoring utilities\n * - Tailwind CSS - Styling\n */\n\nimport React, { Component, ReactNode } from 'react';\nimport { performanceBudgetMonitor } from '../../utils/performance/performanceBudgets';\nimport { logger } from '../../utils/core/logger';\n\n/**\n * State interface for the ErrorBoundary component\n * @public\n */\nexport interface ErrorBoundaryState {\n /** Whether an error has been caught */\n hasError: boolean;\n /** The error that was caught */\n error?: Error;\n /** Additional error information from React */\n errorInfo?: React.ErrorInfo;\n /** Unique identifier for the error */\n errorId?: string;\n /** Number of retry attempts made */\n retryCount: number;\n}\n\n/**\n * Props interface for the ErrorBoundary component\n * @public\n */\nexport interface ErrorBoundaryProps {\n /** Child components to wrap with error boundary */\n children: ReactNode;\n /** Name of the component for error reporting */\n componentName?: string;\n /** Custom fallback UI to display when error occurs */\n fallback?: ReactNode;\n /** Callback function called when an error is caught */\n onError?: (error: Error, errorInfo: React.ErrorInfo, errorId: string) => void;\n /** Maximum number of retry attempts */\n maxRetries?: number;\n /** Whether to enable retry functionality */\n enableRetry?: boolean;\n /** Whether to enable error reporting */\n enableReporting?: boolean;\n /** Internal: Global error handler from context (not part of public API) */\n _globalErrorHandler?: (error: Error, errorInfo: React.ErrorInfo, errorId: string, componentName: string) => void;\n}\n\n/**\n * ErrorBoundary component\n * Catches JavaScript errors in child components and provides fallback UI\n * \n * @example\n * ```tsx\n * <ErrorBoundary\n * componentName=\"MyComponent\"\n * maxRetries={3}\n * onError={(error, errorInfo, errorId) => {\n * console.log('Error caught:', errorId);\n * }}\n * >\n * <MyComponent />\n * </ErrorBoundary>\n * ```\n */\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n private retryTimeoutId: NodeJS.Timeout | null = null;\n\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = { \n hasError: false, \n retryCount: 0 \n };\n }\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n const errorId = `error_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n return { \n hasError: true, \n error,\n errorId \n };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n const { componentName = 'Unknown Component', onError, enableReporting = true } = this.props;\n const errorId = this.state.errorId!;\n const componentNameForHandler = componentName || 'Unknown Component';\n \n this.setState({ errorInfo });\n \n // Enhanced logging with component name and error ID\n logger.error('ErrorBoundary', `[${componentNameForHandler}] Caught error ${errorId}:`, error, errorInfo);\n \n // Performance monitoring - track error occurrence\n performanceBudgetMonitor.measure('ERROR_BOUNDARY_TRIGGER', 1, {\n componentName: componentNameForHandler,\n errorId,\n errorMessage: error.message,\n stack: error.stack?.substring(0, 200), // Truncated stack trace\n });\n\n // Report error if enabled\n if (enableReporting) {\n this.reportError(errorId, componentNameForHandler);\n }\n \n // Call error handler: prefer prop onError, fall back to global handler from props\n if (onError) {\n onError(error, errorInfo, errorId);\n } else if (this.props._globalErrorHandler) {\n this.props._globalErrorHandler(error, errorInfo, errorId, componentNameForHandler);\n }\n }\n\n private reportError = (errorId: string, componentName: string) => {\n // In production, this would send to error reporting service\n if (import.meta.env.MODE === 'production') {\n // Example: Send to error reporting service\n // errorReportingService.report({ error, errorInfo, errorId, componentName });\n logger.warn('ErrorBoundary', 'Error reporting would be triggered in production:', { errorId, componentName });\n }\n };\n\n private handleRetry = () => {\n const { maxRetries = 3 } = this.props;\n const { retryCount } = this.state;\n\n if (retryCount < maxRetries) {\n logger.debug('ErrorBoundary', `Retrying component render (attempt ${retryCount + 1}/${maxRetries})`);\n \n this.setState(prevState => ({\n hasError: false,\n error: undefined,\n errorInfo: undefined,\n errorId: undefined,\n retryCount: prevState.retryCount + 1\n }));\n }\n };\n\n componentWillUnmount() {\n if (this.retryTimeoutId) {\n clearTimeout(this.retryTimeoutId);\n }\n }\n\n render() {\n if (this.state.hasError) {\n const { \n componentName = 'Component', \n fallback, \n enableRetry = true, \n maxRetries = 3 \n } = this.props;\n const { retryCount, errorId } = this.state;\n\n // Use custom fallback if provided\n if (fallback) {\n return fallback;\n }\n\n // Enhanced error UI with retry functionality\n return (\n <div \n role=\"alert\" \n className=\"p-6 bg-destructive/10 border border-destructive/20 rounded-lg\"\n data-error-boundary={errorId}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-shrink-0\">\n <svg className=\"w-5 h-5 text-destructive\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fillRule=\"evenodd\" d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z\" clipRule=\"evenodd\" />\n </svg>\n </div>\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"text-destructive\">\n Error in {componentName}\n </h3>\n <p className=\"text-destructive/80\">\n {this.state.error?.message || 'An unexpected error occurred.'}\n </p>\n \n {enableRetry && retryCount < maxRetries && (\n <div className=\"flex gap-3 mb-4\">\n <button\n onClick={this.handleRetry}\n className=\"px-4 py-2 bg-destructive text-destructive-foreground rounded-md hover:bg-destructive/90 transition-colors text-sm font-medium\"\n >\n Retry ({retryCount + 1}/{maxRetries})\n </button>\n <button\n onClick={() => window.location.reload()}\n className=\"px-4 py-2 bg-sec-600 text-main-50 rounded-md hover:bg-sec-700 transition-colors text-sm font-medium\"\n >\n Reload Page\n </button>\n </div>\n )}\n\n {retryCount >= maxRetries && (\n <div className=\"mb-4 p-3 bg-acc-50 border border-acc-200 rounded-md\">\n <p className=\"text-acc-800\">\n Maximum retry attempts reached. Please reload the page or contact support.\n </p>\n <button\n onClick={() => window.location.reload()}\n className=\"mt-2 px-3 py-1 bg-acc-600 text-main-50 rounded text-sm hover:bg-acc-700\"\n >\n Reload Page\n </button>\n </div>\n )}\n\n {import.meta.env.MODE === 'development' && this.state.error && (\n <details className=\"text-sm text-destructive/70\">\n <summary className=\"cursor-pointer font-medium mb-2\">\n Error Details (Development)\n </summary>\n <div className=\"bg-destructive/5 p-3 rounded border\">\n <p className=\"font-mono\">Error ID: {errorId}</p>\n <pre className=\"whitespace-pre-wrap text-xs overflow-auto max-h-32\">\n {this.state.error.toString()}\n {this.state.errorInfo?.componentStack}\n </pre>\n </div>\n </details>\n )}\n </div>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\nexport default ErrorBoundary;\n","/**\n * @file Error Boundary exports\n */\n\n// Re-export ErrorBoundary with automatic context support\nimport { ErrorBoundary as ErrorBoundaryClass } from './ErrorBoundary';\nimport { useErrorBoundaryContext } from './ErrorBoundaryContext';\nimport React from 'react';\nimport type { ErrorBoundaryProps } from './ErrorBoundary';\n\n// Export types and provider\nexport type { ErrorBoundaryProps, ErrorBoundaryState } from './ErrorBoundary';\nexport { ErrorBoundaryProvider } from './ErrorBoundaryContext';\nexport type { ErrorBoundaryProviderProps, GlobalErrorHandler } from './ErrorBoundaryContext';\n\n/**\n * ErrorBoundary component with automatic context support\n * This wrapper automatically uses the global error handler from ErrorBoundaryProvider\n * if no onError prop is provided.\n */\nexport const ErrorBoundary = React.forwardRef<\n ErrorBoundaryClass,\n Omit<ErrorBoundaryProps, '_globalErrorHandler'>\n>((props, ref) => {\n const context = useErrorBoundaryContext();\n const globalErrorHandler = context?.defaultErrorHandler;\n \n return React.createElement(ErrorBoundaryClass, { ...props, ref, _globalErrorHandler: globalErrorHandler });\n});\n\nErrorBoundary.displayName = 'ErrorBoundary';\n\nexport default ErrorBoundary;\n","/**\n * @file Public Page Provider\n * @package @jmruthers/pace-core\n * @module Components/PublicLayout\n * @since 1.0.0\n *\n * A completely isolated provider for public pages that doesn't trigger\n * any authentication context. This ensures public pages work independently\n * of the main application's authentication system.\n *\n * Features:\n * - No authentication required\n * - No organisation context\n * - No event context\n * - Completely isolated from main app context\n * - Environment variable access for public data\n * - Error boundary integration\n *\n * @example\n * ```tsx\n * import { PublicPageProvider } from '@jmruthers/pace-core';\n * \n * const APP_NAME = 'CORE';\n * \n * function PublicApp() {\n * return (\n * <PublicPageProvider appName={APP_NAME}>\n * <Routes>\n * <Route path=\"/events/:eventCode/recipe-grid-report\" element={<PublicRecipePage />} />\n * </Routes>\n * </PublicPageProvider>\n * );\n * }\n * ```\n */\n\nimport React, { createContext, useContext, ReactNode, useMemo } from 'react';\nimport { createClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { ErrorBoundary } from '../ErrorBoundary';\nimport { logger } from '../../utils/core/logger';\n\ninterface PublicPageContextType {\n isPublicPage: true;\n supabase: ReturnType<typeof createClient<Database>> | null;\n appName: string | null;\n environment: {\n supabaseUrl: string | null;\n supabaseKey: string | null;\n };\n}\n\n/**\n * Context for public pages.\n * Provides isolated context for public pages without authentication requirements.\n */\nexport const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);\n\nexport interface PublicPageProviderProps {\n children: ReactNode;\n /** Application name for logo display and branding. Should match the APP_NAME constant used in App.tsx */\n appName?: string;\n}\n\n/**\n * Provider for public pages that completely isolates them from authentication context\n * \n * This provider:\n * - Does not initialize any authentication providers\n * - Provides environment variables for public data access\n * - Includes error boundary for graceful error handling\n * - Is completely separate from the main app context\n * - Provides appName for consistent logo display\n */\nexport function PublicPageProvider({ children, appName }: PublicPageProviderProps) {\n // Get environment variables for public data access\n // Handle both Vite (import.meta.env) and Node.js (process.env) environments\n const getEnvVar = (key: string): string | undefined => {\n // Check Vite environment first (browser)\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const env = import.meta.env as Record<string, string | undefined>;\n return env[key];\n }\n // Check Node.js environment (server-side)\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key];\n }\n return undefined;\n };\n\n const supabaseUrl = getEnvVar('VITE_SUPABASE_URL') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_URL') || \n null;\n \n const supabaseKey = getEnvVar('VITE_SUPABASE_PUBLISHABLE_KEY') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY') ||\n getEnvVar('VITE_SUPABASE_ANON_KEY') ||\n getEnvVar('NEXT_PUBLIC_SUPABASE_ANON_KEY') || \n null;\n\n // Create Supabase client if environment variables are available\n // Note: VITE_SUPABASE_PUBLISHABLE_KEY should be your publishable key (sb_publishable_...)\n const supabase = useMemo(() => {\n if (!supabaseUrl || !supabaseKey) {\n logger.warn('PublicPageProvider', 'Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY are set in your environment.');\n return null;\n }\n const client = createClient<Database>(supabaseUrl, supabaseKey);\n return client;\n }, [supabaseUrl, supabaseKey]);\n\n const contextValue: PublicPageContextType = {\n isPublicPage: true,\n supabase,\n appName: appName || null,\n environment: {\n supabaseUrl,\n supabaseKey\n }\n };\n\n return (\n <PublicPageContext.Provider value={contextValue}>\n <ErrorBoundary componentName=\"PublicPageProvider\">\n {children}\n </ErrorBoundary>\n </PublicPageContext.Provider>\n );\n}\n\n/**\n * Hook to access public page context\n * \n * @returns Public page context with environment variables\n */\nexport function usePublicPageContext(): PublicPageContextType {\n const context = useContext(PublicPageContext);\n \n if (!context) {\n throw new Error('usePublicPageContext must be used within a PublicPageProvider');\n }\n \n return context;\n}\n\n/**\n * Hook to check if we're in a public page context\n * \n * @returns True if we're in a public page context\n */\nexport function useIsPublicPage(): boolean {\n const context = useContext(PublicPageContext);\n return context !== undefined;\n}\n","/**\n * @file useAppConfig Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useAppConfig\n * @since 0.4.0\n *\n * Hook for accessing app configuration like direct access support and event requirements.\n * This is a convenience hook that extracts app config from the UnifiedAuthProvider.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { supportsDirectAccess, requiresEvent, isLoading } = useAppConfig();\n * \n * if (isLoading) return <div>Loading...</div>;\n * \n * return (\n * <div>\n * {supportsDirectAccess && (\n * <div>This app supports direct access!</div>\n * )}\n * {requiresEvent && (\n * <EventSelector />\n * )}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { useMemo, useContext } from 'react';\nimport { useUnifiedAuth } from '../providers/services/UnifiedAuthProvider';\nimport { useIsPublicPage, PublicPageContext } from '../components/PublicLayout/PublicPageProvider';\n\nexport interface UseAppConfigReturn {\n supportsDirectAccess: boolean;\n requiresEvent: boolean;\n isLoading: boolean;\n appName: string;\n}\n\n/**\n * Hook to access app configuration\n * Works in both authenticated and public contexts\n * @returns App configuration and loading state\n */\nexport function useAppConfig(): UseAppConfigReturn {\n // Check if we're in a public page context first\n const isPublicPage = useIsPublicPage();\n \n // Try to get appName from PublicPageContext (access context directly to avoid hook rule violations)\n const publicPageContext = useContext(PublicPageContext);\n const hasPublicContext = publicPageContext !== undefined;\n const contextAppName = publicPageContext?.appName || null;\n const getNodeEnvVar = (key: string): string | undefined =>\n typeof process !== 'undefined' && process.env ? process.env[key] : undefined;\n \n if (isPublicPage) {\n const getAppName = (): string => {\n // When tests mock public mode without the provider, default to PACE\n if (!hasPublicContext) {\n return 'PACE';\n }\n \n // Priority 1: Use appName from PublicPageContext (passed from App.tsx)\n if (contextAppName) {\n return contextAppName;\n }\n \n // Priority 2: Check environment variables\n if (typeof import.meta !== 'undefined' && (import.meta as any).env) {\n const env = (import.meta as any).env;\n return env.VITE_APP_NAME || \n env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.env.VITE_APP_NAME || \n import.meta.env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n \n const nodeEnvAppName =\n getNodeEnvVar('VITE_APP_NAME') ||\n getNodeEnvVar('NEXT_PUBLIC_APP_NAME');\n if (nodeEnvAppName) {\n return nodeEnvAppName;\n }\n \n // Priority 3: Default fallback\n return 'PACE';\n };\n \n return useMemo(() => ({\n supportsDirectAccess: false, // Public pages don't support direct access\n requiresEvent: true, // Public pages always require an event\n isLoading: false,\n appName: getAppName()\n }), [contextAppName, hasPublicContext]);\n }\n \n // For authenticated pages, use UnifiedAuthProvider\n try {\n const { appConfig, appName } = useUnifiedAuth();\n return useMemo(() => ({\n supportsDirectAccess: !(appConfig?.requires_event ?? true),\n requiresEvent: appConfig?.requires_event ?? true,\n isLoading: appConfig === null,\n appName\n }), [appConfig?.requires_event, appName]);\n } catch (error) {\n // Fallback if UnifiedAuthProvider is not available\n return useMemo(() => ({\n supportsDirectAccess: false,\n requiresEvent: true,\n isLoading: false,\n appName: contextAppName ||\n getNodeEnvVar('VITE_APP_NAME') ||\n getNodeEnvVar('NEXT_PUBLIC_APP_NAME') ||\n 'PACE'\n }), [contextAppName]);\n }\n} ","/**\n * @file Organisation Security Hook\n * @package @jmruthers/pace-core\n * @module Hooks/OrganisationSecurity\n * @since 0.4.0\n *\n * Security-focused hook for organisation access validation and super admin functionality.\n * Provides utilities for validating user access to organisations and checking permissions.\n */\n\nimport { useCallback, useMemo, useEffect, useState } from 'react';\nimport { useUnifiedAuth } from '../providers';\nimport { useOrganisations } from './useOrganisations';\n// Legacy useRBAC hook removed - use new RBAC system instead\nimport type { OrganisationSecurityError, SuperAdminContext } from '../types/organisation';\nimport type { Permission } from '../rbac/types';\nimport { logger } from '../utils/core/logger';\n\n/**\n * Organisation security hook interface.\n * Provides security utilities for organisation access validation and super admin functionality.\n */\nexport interface OrganisationSecurityHook {\n // Super admin context\n superAdminContext: SuperAdminContext;\n \n // Access validation\n validateOrganisationAccess: (orgId: string) => Promise<boolean>;\n hasMinimumRole: (minRole: string, orgId?: string) => boolean;\n canAccessChildOrganisations: (orgId?: string) => boolean;\n \n // Permission checks\n hasPermission: (permission: string, orgId?: string) => Promise<boolean>;\n getUserPermissions: (orgId?: string) => Promise<string[]>;\n \n // Audit logging\n logOrganisationAccess: (action: string, details?: Record<string, unknown>) => Promise<void>;\n \n // Security utilities\n ensureOrganisationAccess: (orgId: string) => Promise<void>;\n validateUserAccess: (userId: string, orgId: string) => Promise<boolean>;\n}\n\nexport const useOrganisationSecurity = (): OrganisationSecurityHook => {\n const { user, session, supabase } = useUnifiedAuth();\n const { selectedOrganisation, getUserRole, validateOrganisationAccess: validateAccess } = useOrganisations();\n \n // Super admin status - query database for security (user_metadata can be spoofed)\n const [isSuperAdmin, setIsSuperAdmin] = useState<boolean>(false);\n const [isCheckingSuperAdmin, setIsCheckingSuperAdmin] = useState(false);\n\n // Check super admin status from database\n useEffect(() => {\n if (!user || !session || !supabase) {\n setIsSuperAdmin(false);\n return;\n }\n\n const checkSuperAdmin = async () => {\n setIsCheckingSuperAdmin(true);\n try {\n const now = new Date().toISOString();\n const { data, error } = await supabase\n .from('rbac_global_roles')\n .select('role')\n .eq('user_id', user.id)\n .eq('role', 'super_admin')\n .lte('valid_from', now)\n .or(`valid_to.is.null,valid_to.gte.${now}`)\n .limit(1);\n\n setIsSuperAdmin(!error && data && data.length > 0);\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Error checking super admin status:', error);\n setIsSuperAdmin(false);\n } finally {\n setIsCheckingSuperAdmin(false);\n }\n };\n\n checkSuperAdmin();\n }, [user, session, supabase]);\n\n // Super admin context\n const superAdminContext = useMemo((): SuperAdminContext => {\n return {\n isSuperAdmin,\n hasGlobalAccess: isSuperAdmin,\n canManageAllOrganisations: isSuperAdmin\n };\n }, [isSuperAdmin]);\n\n // Validate organisation access with database check\n const validateOrganisationAccess = useCallback(async (orgId: string): Promise<boolean> => {\n if (!user || !session || !supabase) return false;\n \n try {\n // Super admin has access to all organisations\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n // Check organisation membership using consolidated rbac_organisation_roles table\n const { data, error } = await supabase\n .from('rbac_organisation_roles')\n .select('id')\n .eq('user_id', user.id)\n .eq('organisation_id', orgId)\n .eq('status', 'active')\n .is('revoked_at', null)\n .in('role', ['org_admin', 'leader', 'member']) // Only actual members, not supporters\n .single();\n\n if (error) {\n logger.error('useOrganisationSecurity', 'Error validating organisation access:', error);\n return false;\n }\n\n return !!data;\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception validating organisation access:', error);\n return false;\n }\n }, [user, session, supabase, superAdminContext.isSuperAdmin]);\n\n // Check if user has minimum role\n const hasMinimumRole = useCallback((minRole: string, orgId?: string): boolean => {\n // Super admin has all roles\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId) return false;\n\n const userRole = getUserRole(targetOrgId);\n const roleHierarchy = ['supporter', 'member', 'leader', 'org_admin'];\n \n const userRoleIndex = roleHierarchy.indexOf(userRole);\n const minRoleIndex = roleHierarchy.indexOf(minRole);\n \n return userRoleIndex >= minRoleIndex;\n }, [selectedOrganisation, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Check if user can access child organisations\n const canAccessChildOrganisations = useCallback((orgId?: string): boolean => {\n // Super admin can access all organisations\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId) return false;\n\n const userRole = getUserRole(targetOrgId);\n return userRole === 'org_admin';\n }, [selectedOrganisation, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Check specific permission using the new RBAC system\n const hasPermission = useCallback(async (permission: string, orgId?: string): Promise<boolean> => {\n // Super admin has all permissions\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId || !user) return false;\n\n try {\n // Use the new RBAC system with caching\n const { isPermittedCached } = await import('../rbac/api');\n \n const scope = {\n organisationId: targetOrgId,\n eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,\n appId: user.user_metadata?.appId || user.app_metadata?.appId,\n };\n\n return await isPermittedCached({\n userId: user.id,\n scope,\n permission: permission as Permission\n });\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception checking permission:', error);\n return false;\n }\n }, [selectedOrganisation, user, superAdminContext.isSuperAdmin]);\n\n // Get user's permissions for organisation using the new RBAC system\n const getUserPermissions = useCallback(async (orgId?: string): Promise<string[]> => {\n // Super admin has all permissions\n if (superAdminContext.isSuperAdmin) {\n return ['*']; // All permissions\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId || !user) return [];\n\n try {\n // Use the new RBAC system\n const { getPermissionMap } = await import('../rbac/api');\n \n const scope = {\n organisationId: targetOrgId,\n eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,\n appId: user.user_metadata?.appId || user.app_metadata?.appId,\n };\n\n const permissionMap = await getPermissionMap({\n userId: user.id,\n scope\n });\n \n // Flatten all permissions from all pages\n const allPermissions = Object.entries(permissionMap)\n .filter(([, allowed]) => allowed)\n .map(([permission]) => permission);\n return [...new Set(allPermissions)]; // Remove duplicates\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception getting user permissions:', error);\n return [];\n }\n }, [selectedOrganisation, user, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Log organisation access for audit using the new RBAC audit system\n const logOrganisationAccess = useCallback(async (action: string, details?: Record<string, unknown>): Promise<void> => {\n if (!user || !selectedOrganisation) return;\n\n try {\n // Use the new RBAC audit system - only if we have a valid organisation ID\n if (selectedOrganisation.id) {\n const { emitAuditEvent } = await import('../rbac/audit');\n \n await emitAuditEvent({\n type: 'permission_check',\n userId: user.id,\n organisationId: selectedOrganisation.id,\n permission: action,\n decision: true, // Assume access was granted if we're logging it\n source: 'api',\n duration_ms: 0, // No actual permission check performed here\n metadata: details || {}\n });\n }\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Error logging organisation access:', error);\n }\n }, [user, selectedOrganisation]);\n\n // Ensure organisation access (throws if no access)\n const ensureOrganisationAccess = useCallback(async (orgId: string): Promise<void> => {\n const hasAccess = await validateOrganisationAccess(orgId);\n \n if (!hasAccess) {\n const error = new Error(`User does not have access to organisation ${orgId}`) as OrganisationSecurityError;\n error.name = 'OrganisationSecurityError';\n error.code = 'ACCESS_DENIED';\n error.organisationId = orgId;\n error.userId = user?.id;\n throw error;\n }\n }, [validateOrganisationAccess, user]);\n\n // Validate user access (for admin functions)\n const validateUserAccess = useCallback(async (userId: string, orgId: string): Promise<boolean> => {\n if (!supabase) return false;\n\n try {\n // Super admin can validate any user\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n // Regular users can only validate their own access\n if (userId !== user?.id) {\n return false;\n }\n\n return await validateOrganisationAccess(orgId);\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception validating user access:', error);\n return false;\n }\n }, [supabase, superAdminContext.isSuperAdmin, user, validateOrganisationAccess]);\n\n return {\n superAdminContext,\n validateOrganisationAccess,\n hasMinimumRole,\n canAccessChildOrganisations,\n hasPermission,\n getUserPermissions,\n logOrganisationAccess,\n ensureOrganisationAccess,\n validateUserAccess\n };\n}; "],"mappings":"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","names":["jsx","React","ErrorBoundary","createContext","useContext","useMemo","jsx","createContext","useMemo","ErrorBoundary","useContext","useMemo","useContext","useContext","useMemo","useMemo","useMemo"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useEvents.ts","../src/components/ErrorBoundary/ErrorBoundaryContext.tsx","../src/components/ErrorBoundary/ErrorBoundary.tsx","../src/components/ErrorBoundary/index.ts","../src/components/PublicLayout/PublicPageProvider.tsx","../src/hooks/useAppConfig.ts","../src/hooks/useOrganisationSecurity.ts"],"sourcesContent":["/**\n * @file Event Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.1.0\n *\n * Convenience hook for accessing event context.\n * This hook provides a simple interface for working with events.\n * \n * Note: This is a convenience wrapper around the EventService.\n * For better performance, consider using useEventService directly.\n */\n\nimport { useMemo, useRef } from 'react';\nimport { useEventService } from './services/useEventService';\nimport { Event } from '../types/event';\n\nexport interface EventContextType {\n events: Event[];\n selectedEvent: Event | null;\n isLoading: boolean;\n error: Error | null;\n setSelectedEvent: (event: Event | null) => void;\n refreshEvents: () => Promise<void>;\n clearEventSelection: () => Promise<void>;\n}\n\n/**\n * Hook to access event context\n * \n * @returns Event context with events, selected event, and helper methods\n * @throws {Error} If used outside EventServiceProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { events, selectedEvent, setSelectedEvent } = useEvents();\n * \n * return (\n * <div>\n * {events.map(event => (\n * <button key={event.id} onClick={() => setSelectedEvent(event)}>\n * {event.event_name}\n * </button>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useEvents(): EventContextType {\n const eventService = useEventService();\n\n // Get current state - service subscription will trigger re-renders when state changes\n const rawEvents = eventService.getEvents();\n const selectedEvent = eventService.getSelectedEvent();\n const isLoading = eventService.isLoading();\n const error = eventService.getError();\n\n // Use refs to track previous values and only create new array reference when events actually change\n const prevEventsRef = useRef<Event[]>([]);\n const prevEventsIdsRef = useRef<string>('');\n \n // Compare event IDs to detect actual changes (more efficient than deep comparison)\n // Do this outside useMemo to avoid dependency on rawEvents array\n const currentEventsIds = rawEvents.map(e => e.event_id || e.id).join(',');\n const eventsChanged = currentEventsIds !== prevEventsIdsRef.current;\n \n // Only create new array reference when events actually changed\n // Update refs synchronously, then memoize based on the ID string\n if (eventsChanged) {\n prevEventsRef.current = rawEvents;\n prevEventsIdsRef.current = currentEventsIds;\n }\n \n // Return stable array reference - only changes when event IDs change\n const events = useMemo(() => {\n return prevEventsRef.current;\n }, [currentEventsIds]);\n\n // Memoize callbacks to prevent unnecessary re-renders in child components\n const setSelectedEventCallback = useMemo(\n () => (event: Event | null) => eventService.setSelectedEvent(event),\n [eventService]\n );\n const refreshEventsCallback = useMemo(\n () => () => eventService.refreshEvents(),\n [eventService]\n );\n const clearEventSelectionCallback = useMemo(\n () => () => eventService.clearEventSelection(),\n [eventService]\n );\n\n // Memoize the return object - only recreate when actual values change\n return useMemo(() => ({\n events,\n selectedEvent,\n isLoading,\n error,\n setSelectedEvent: setSelectedEventCallback,\n refreshEvents: refreshEventsCallback,\n clearEventSelection: clearEventSelectionCallback,\n }), [events, selectedEvent?.event_id, isLoading, error?.message, setSelectedEventCallback, refreshEventsCallback, clearEventSelectionCallback]);\n}\n\n","/**\n * @file Error Boundary Context\n * @package @jmruthers/pace-core\n * @module Components/ErrorBoundary\n * @since 0.6.2\n *\n * Context provider for global error handler configuration in ErrorBoundary components.\n * Allows applications to register a default error handler once, which is then used by\n * all ErrorBoundary instances unless overridden.\n *\n * @example\n * ```tsx\n * // Setup once in main.tsx\n * <ErrorBoundaryProvider\n * defaultErrorHandler={(error, errorInfo, errorId, componentName) => {\n * if (import.meta.env.DEV) {\n * console.error(`[${componentName}] ErrorBoundary caught error:`, error, errorInfo);\n * }\n * errorTracking.captureException(error, {\n * componentStack: errorInfo.componentStack,\n * errorBoundary: true,\n * errorId,\n * componentName,\n * });\n * }}\n * >\n * <App />\n * </ErrorBoundaryProvider>\n *\n * // Usage - no onError needed, uses global handler\n * <ErrorBoundary componentName=\"AppRoot\">\n * <AppContent />\n * </ErrorBoundary>\n * ```\n */\n\nimport React, { createContext, useContext, ReactNode } from 'react';\n\n/**\n * Type definition for the global error handler function\n * @public\n */\nexport type GlobalErrorHandler = (\n error: Error,\n errorInfo: React.ErrorInfo,\n errorId: string,\n componentName: string\n) => void;\n\n/**\n * Context value type for ErrorBoundary configuration\n * @internal\n */\ninterface ErrorBoundaryContextType {\n /** Global error handler that will be used by all ErrorBoundary instances */\n defaultErrorHandler?: GlobalErrorHandler;\n}\n\n/**\n * Context for ErrorBoundary global configuration\n * @internal\n */\nconst ErrorBoundaryContext = createContext<ErrorBoundaryContextType | undefined>(undefined);\n\n/**\n * Props for ErrorBoundaryProvider component\n * @public\n */\nexport interface ErrorBoundaryProviderProps {\n /** Children to wrap with the provider */\n children: ReactNode;\n /** Global error handler that will be used by all ErrorBoundary instances unless overridden */\n defaultErrorHandler?: GlobalErrorHandler;\n}\n\n/**\n * Provider component for ErrorBoundary global error handler configuration\n * \n * Wrap your application with this provider to set a default error handler that will\n * be used by all ErrorBoundary instances unless they provide their own onError prop.\n * \n * @example\n * ```tsx\n * <ErrorBoundaryProvider\n * defaultErrorHandler={(error, errorInfo, errorId, componentName) => {\n * errorTracking.captureException(error, {\n * componentStack: errorInfo.componentStack,\n * errorBoundary: true,\n * errorId,\n * componentName,\n * });\n * }}\n * >\n * <App />\n * </ErrorBoundaryProvider>\n * ```\n * \n * @public\n */\n/**\n * Error boundary provider component.\n * Provides global error handling configuration to child ErrorBoundary components.\n * \n * @param props - Error boundary provider configuration\n * @returns The error boundary provider\n */\nexport function ErrorBoundaryProvider({\n children,\n defaultErrorHandler,\n}: ErrorBoundaryProviderProps) {\n const contextValue: ErrorBoundaryContextType = {\n defaultErrorHandler,\n };\n\n return (\n <ErrorBoundaryContext.Provider value={contextValue}>\n {children}\n </ErrorBoundaryContext.Provider>\n );\n}\n\n/**\n * Hook to access ErrorBoundary context\n * @internal\n */\nexport function useErrorBoundaryContext(): ErrorBoundaryContextType | undefined {\n return useContext(ErrorBoundaryContext);\n}\n\n","/**\n * @file Error Boundary Component\n * @package @jmruthers/pace-core\n * @module Components/ErrorBoundary\n * @since 0.1.0\n *\n * A comprehensive error boundary component that catches JavaScript errors in child components\n * and provides fallback UI with retry functionality and error reporting.\n *\n * Features:\n * - Catches JavaScript errors in component tree\n * - Custom fallback UI with retry functionality\n * - Error reporting and logging\n * - Performance monitoring integration\n * - Retry mechanism with configurable attempts\n * - Development mode error details\n * - Accessibility compliant error display\n * - Component-specific error tracking\n * - Error ID generation for tracking\n *\n * @example\n * ```tsx\n * // Basic error boundary\n * <ErrorBoundary>\n * <MyComponent />\n * </ErrorBoundary>\n * \n * // Using global error handler (recommended for applications)\n * import { ErrorBoundaryProvider, ErrorBoundary } from '@jmruthers/pace-core';\n * \n * // Setup once in main.tsx\n * <ErrorBoundaryProvider\n * defaultErrorHandler={(error, errorInfo, errorId, componentName) => {\n * if (import.meta.env.DEV) {\n * console.error(`[${componentName}] ErrorBoundary caught error:`, error, errorInfo);\n * }\n * errorTracking.captureException(error, {\n * componentStack: errorInfo.componentStack,\n * errorBoundary: true,\n * errorId,\n * componentName,\n * });\n * }}\n * >\n * <App />\n * </ErrorBoundaryProvider>\n * \n * // Then use ErrorBoundary without onError prop - uses global handler\n * <ErrorBoundary componentName=\"AppRoot\">\n * <AppContent />\n * </ErrorBoundary>\n * \n * // Error boundary with custom fallback\n * <ErrorBoundary\n * componentName=\"UserProfile\"\n * fallback={<section>Something went wrong loading the profile.</section>}\n * onError={(error, errorInfo, errorId) => {\n * console.log('Error caught:', errorId, error);\n * }}\n * >\n * <UserProfile />\n * </ErrorBoundary>\n * \n * // Error boundary with retry functionality\n * <ErrorBoundary\n * componentName=\"DataTable\"\n * maxRetries={3}\n * enableRetry={true}\n * enableReporting={true}\n * onError={(error, errorInfo, errorId) => {\n * // Send to error reporting service\n * errorReportingService.report({ error, errorInfo, errorId });\n * }}\n * >\n * <DataTable data={data} />\n * </ErrorBoundary>\n * \n * // Wrapping multiple components\n * <ErrorBoundary componentName=\"App\">\n * <Header />\n * <ErrorBoundary componentName=\"MainContent\">\n * <MainContent />\n * </ErrorBoundary>\n * <Footer />\n * </ErrorBoundary>\n * \n * // Per-instance onError overrides global handler\n * <ErrorBoundary \n * componentName=\"SpecialCase\"\n * onError={(error, errorInfo, errorId) => {\n * // Custom handling for this specific case - global handler won't be called\n * }}\n * >\n * <SpecialComponent />\n * </ErrorBoundary>\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Proper ARIA role=\"alert\" for error announcements\n * - Screen reader friendly error messages\n * - Keyboard accessible retry buttons\n * - High contrast error styling\n * - Clear error identification\n *\n * @performance\n * - Efficient error state management\n * - Performance monitoring integration\n * - Minimal impact on normal rendering\n * - Configurable error reporting\n *\n * @dependencies\n * - React 19+ - Component lifecycle\n * - Performance monitoring utilities\n * - Tailwind CSS - Styling\n */\n\nimport React, { Component, ReactNode } from 'react';\nimport { performanceBudgetMonitor } from '../../utils/performance/performanceBudgets';\nimport { logger } from '../../utils/core/logger';\n\n/**\n * State interface for the ErrorBoundary component\n * @public\n */\nexport interface ErrorBoundaryState {\n /** Whether an error has been caught */\n hasError: boolean;\n /** The error that was caught */\n error?: Error;\n /** Additional error information from React */\n errorInfo?: React.ErrorInfo;\n /** Unique identifier for the error */\n errorId?: string;\n /** Number of retry attempts made */\n retryCount: number;\n}\n\n/**\n * Props interface for the ErrorBoundary component\n * @public\n */\nexport interface ErrorBoundaryProps {\n /** Child components to wrap with error boundary */\n children: ReactNode;\n /** Name of the component for error reporting */\n componentName?: string;\n /** Custom fallback UI to display when error occurs */\n fallback?: ReactNode;\n /** Callback function called when an error is caught */\n onError?: (error: Error, errorInfo: React.ErrorInfo, errorId: string) => void;\n /** Maximum number of retry attempts */\n maxRetries?: number;\n /** Whether to enable retry functionality */\n enableRetry?: boolean;\n /** Whether to enable error reporting */\n enableReporting?: boolean;\n /** Internal: Global error handler from context (not part of public API) */\n _globalErrorHandler?: (error: Error, errorInfo: React.ErrorInfo, errorId: string, componentName: string) => void;\n}\n\n/**\n * ErrorBoundary component\n * Catches JavaScript errors in child components and provides fallback UI\n * \n * @example\n * ```tsx\n * <ErrorBoundary\n * componentName=\"MyComponent\"\n * maxRetries={3}\n * onError={(error, errorInfo, errorId) => {\n * console.log('Error caught:', errorId);\n * }}\n * >\n * <MyComponent />\n * </ErrorBoundary>\n * ```\n */\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n private retryTimeoutId: NodeJS.Timeout | null = null;\n\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = { \n hasError: false, \n retryCount: 0 \n };\n }\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n const errorId = `error_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n return { \n hasError: true, \n error,\n errorId \n };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n const { componentName = 'Unknown Component', onError, enableReporting = true } = this.props;\n const errorId = this.state.errorId!;\n const componentNameForHandler = componentName || 'Unknown Component';\n \n this.setState({ errorInfo });\n \n // Enhanced logging with component name and error ID\n logger.error('ErrorBoundary', `[${componentNameForHandler}] Caught error ${errorId}:`, error, errorInfo);\n \n // Performance monitoring - track error occurrence\n performanceBudgetMonitor.measure('ERROR_BOUNDARY_TRIGGER', 1, {\n componentName: componentNameForHandler,\n errorId,\n errorMessage: error.message,\n stack: error.stack?.substring(0, 200), // Truncated stack trace\n });\n\n // Report error if enabled\n if (enableReporting) {\n this.reportError(errorId, componentNameForHandler);\n }\n \n // Call error handler: prefer prop onError, fall back to global handler from props\n if (onError) {\n onError(error, errorInfo, errorId);\n } else if (this.props._globalErrorHandler) {\n this.props._globalErrorHandler(error, errorInfo, errorId, componentNameForHandler);\n }\n }\n\n private reportError = (errorId: string, componentName: string) => {\n // In production, this would send to error reporting service\n if (import.meta.env.MODE === 'production') {\n // Example: Send to error reporting service\n // errorReportingService.report({ error, errorInfo, errorId, componentName });\n logger.warn('ErrorBoundary', 'Error reporting would be triggered in production:', { errorId, componentName });\n }\n };\n\n private handleRetry = () => {\n const { maxRetries = 3 } = this.props;\n const { retryCount } = this.state;\n\n if (retryCount < maxRetries) {\n logger.debug('ErrorBoundary', `Retrying component render (attempt ${retryCount + 1}/${maxRetries})`);\n \n this.setState(prevState => ({\n hasError: false,\n error: undefined,\n errorInfo: undefined,\n errorId: undefined,\n retryCount: prevState.retryCount + 1\n }));\n }\n };\n\n componentWillUnmount() {\n if (this.retryTimeoutId) {\n clearTimeout(this.retryTimeoutId);\n }\n }\n\n render() {\n if (this.state.hasError) {\n const { \n componentName = 'Component', \n fallback, \n enableRetry = true, \n maxRetries = 3 \n } = this.props;\n const { retryCount, errorId } = this.state;\n\n // Use custom fallback if provided\n if (fallback) {\n return fallback;\n }\n\n // Enhanced error UI with retry functionality\n return (\n <div \n role=\"alert\" \n className=\"p-6 bg-destructive/10 border border-destructive/20 rounded-lg\"\n data-error-boundary={errorId}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-shrink-0\">\n <svg className=\"w-5 h-5 text-destructive\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fillRule=\"evenodd\" d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z\" clipRule=\"evenodd\" />\n </svg>\n </div>\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"text-destructive\">\n Error in {componentName}\n </h3>\n <p className=\"text-destructive/80\">\n {this.state.error?.message || 'An unexpected error occurred.'}\n </p>\n \n {enableRetry && retryCount < maxRetries && (\n <div className=\"flex gap-3 mb-4\">\n <button\n onClick={this.handleRetry}\n className=\"px-4 py-2 bg-destructive text-destructive-foreground rounded-md hover:bg-destructive/90 transition-colors text-sm font-medium\"\n >\n Retry ({retryCount + 1}/{maxRetries})\n </button>\n <button\n onClick={() => window.location.reload()}\n className=\"px-4 py-2 bg-sec-600 text-main-50 rounded-md hover:bg-sec-700 transition-colors text-sm font-medium\"\n >\n Reload Page\n </button>\n </div>\n )}\n\n {retryCount >= maxRetries && (\n <div className=\"mb-4 p-3 bg-acc-50 border border-acc-200 rounded-md\">\n <p className=\"text-acc-800\">\n Maximum retry attempts reached. Please reload the page or contact support.\n </p>\n <button\n onClick={() => window.location.reload()}\n className=\"mt-2 px-3 py-1 bg-acc-600 text-main-50 rounded text-sm hover:bg-acc-700\"\n >\n Reload Page\n </button>\n </div>\n )}\n\n {import.meta.env.MODE === 'development' && this.state.error && (\n <details className=\"text-sm text-destructive/70\">\n <summary className=\"cursor-pointer font-medium mb-2\">\n Error Details (Development)\n </summary>\n <div className=\"bg-destructive/5 p-3 rounded border\">\n <p className=\"font-mono\">Error ID: {errorId}</p>\n <pre className=\"whitespace-pre-wrap text-xs overflow-auto max-h-32\">\n {this.state.error.toString()}\n {this.state.errorInfo?.componentStack}\n </pre>\n </div>\n </details>\n )}\n </div>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\nexport default ErrorBoundary;\n","/**\n * @file Error Boundary exports\n */\n\n// Re-export ErrorBoundary with automatic context support\nimport { ErrorBoundary as ErrorBoundaryClass } from './ErrorBoundary';\nimport { useErrorBoundaryContext } from './ErrorBoundaryContext';\nimport React from 'react';\nimport type { ErrorBoundaryProps } from './ErrorBoundary';\n\n// Export types and provider\nexport type { ErrorBoundaryProps, ErrorBoundaryState } from './ErrorBoundary';\nexport { ErrorBoundaryProvider } from './ErrorBoundaryContext';\nexport type { ErrorBoundaryProviderProps, GlobalErrorHandler } from './ErrorBoundaryContext';\n\n/**\n * ErrorBoundary component with automatic context support\n * This wrapper automatically uses the global error handler from ErrorBoundaryProvider\n * if no onError prop is provided.\n */\nexport const ErrorBoundary = React.forwardRef<\n ErrorBoundaryClass,\n Omit<ErrorBoundaryProps, '_globalErrorHandler'>\n>((props, ref) => {\n const context = useErrorBoundaryContext();\n const globalErrorHandler = context?.defaultErrorHandler;\n \n return React.createElement(ErrorBoundaryClass, { ...props, ref, _globalErrorHandler: globalErrorHandler });\n});\n\nErrorBoundary.displayName = 'ErrorBoundary';\n\nexport default ErrorBoundary;\n","/**\n * @file Public Page Provider\n * @package @jmruthers/pace-core\n * @module Components/PublicLayout\n * @since 1.0.0\n *\n * A completely isolated provider for public pages that doesn't trigger\n * any authentication context. This ensures public pages work independently\n * of the main application's authentication system.\n *\n * Features:\n * - No authentication required\n * - No organisation context\n * - No event context\n * - Completely isolated from main app context\n * - Environment variable access for public data\n * - Error boundary integration\n *\n * @example\n * ```tsx\n * import { PublicPageProvider } from '@jmruthers/pace-core';\n * \n * const APP_NAME = 'CORE';\n * \n * function PublicApp() {\n * return (\n * <PublicPageProvider appName={APP_NAME}>\n * <Routes>\n * <Route path=\"/events/:eventCode/recipe-grid-report\" element={<PublicRecipePage />} />\n * </Routes>\n * </PublicPageProvider>\n * );\n * }\n * ```\n */\n\nimport React, { createContext, useContext, ReactNode, useMemo } from 'react';\nimport { createClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { ErrorBoundary } from '../ErrorBoundary';\nimport { logger } from '../../utils/core/logger';\n\ninterface PublicPageContextType {\n isPublicPage: true;\n supabase: ReturnType<typeof createClient<Database>> | null;\n appName: string | null;\n environment: {\n supabaseUrl: string | null;\n supabaseKey: string | null;\n };\n}\n\n/**\n * Context for public pages.\n * Provides isolated context for public pages without authentication requirements.\n */\nexport const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);\n\nexport interface PublicPageProviderProps {\n children: ReactNode;\n /** Application name for logo display and branding. Should match the APP_NAME constant used in App.tsx */\n appName?: string;\n}\n\n/**\n * Provider for public pages that completely isolates them from authentication context\n * \n * This provider:\n * - Does not initialize any authentication providers\n * - Provides environment variables for public data access\n * - Includes error boundary for graceful error handling\n * - Is completely separate from the main app context\n * - Provides appName for consistent logo display\n */\nexport function PublicPageProvider({ children, appName }: PublicPageProviderProps) {\n // Get environment variables for public data access\n // Handle both Vite (import.meta.env) and Node.js (process.env) environments\n const getEnvVar = (key: string): string | undefined => {\n // Check Vite environment first (browser)\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const env = import.meta.env as Record<string, string | undefined>;\n return env[key];\n }\n // Check Node.js environment (server-side)\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key];\n }\n return undefined;\n };\n\n const supabaseUrl = getEnvVar('VITE_SUPABASE_URL') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_URL') || \n null;\n \n const supabaseKey = getEnvVar('VITE_SUPABASE_PUBLISHABLE_KEY') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY') ||\n getEnvVar('VITE_SUPABASE_ANON_KEY') ||\n getEnvVar('NEXT_PUBLIC_SUPABASE_ANON_KEY') || \n null;\n\n // Create Supabase client if environment variables are available\n // Note: VITE_SUPABASE_PUBLISHABLE_KEY should be your publishable key (sb_publishable_...)\n const supabase = useMemo(() => {\n if (!supabaseUrl || !supabaseKey) {\n logger.warn('PublicPageProvider', 'Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY are set in your environment.');\n return null;\n }\n const client = createClient<Database>(supabaseUrl, supabaseKey);\n return client;\n }, [supabaseUrl, supabaseKey]);\n\n const contextValue: PublicPageContextType = {\n isPublicPage: true,\n supabase,\n appName: appName || null,\n environment: {\n supabaseUrl,\n supabaseKey\n }\n };\n\n return (\n <PublicPageContext.Provider value={contextValue}>\n <ErrorBoundary componentName=\"PublicPageProvider\">\n {children}\n </ErrorBoundary>\n </PublicPageContext.Provider>\n );\n}\n\n/**\n * Hook to access public page context\n * \n * @returns Public page context with environment variables\n */\nexport function usePublicPageContext(): PublicPageContextType {\n const context = useContext(PublicPageContext);\n \n if (!context) {\n throw new Error('usePublicPageContext must be used within a PublicPageProvider');\n }\n \n return context;\n}\n\n/**\n * Hook to check if we're in a public page context\n * \n * @returns True if we're in a public page context\n */\nexport function useIsPublicPage(): boolean {\n const context = useContext(PublicPageContext);\n return context !== undefined;\n}\n","/**\n * @file useAppConfig Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useAppConfig\n * @since 0.4.0\n *\n * Hook for accessing app name and loading state.\n * \n * NOTE: Scope configuration (requires_event) is now page-level only (rbac_app_pages.scope_type).\n * Use getPageScopeType() to determine if a specific page requires event or organisation context.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { appName, isLoading } = useAppConfig();\n * \n * if (isLoading) return <div>Loading...</div>;\n * \n * return <div>App: {appName}</div>;\n * }\n * ```\n */\n\nimport { useMemo, useContext } from 'react';\nimport { useUnifiedAuth } from '../providers/services/UnifiedAuthProvider';\nimport { useIsPublicPage, PublicPageContext } from '../components/PublicLayout/PublicPageProvider';\n\nexport interface UseAppConfigReturn {\n appName: string;\n isLoading: boolean;\n}\n\n/**\n * Hook to access app configuration\n * Works in both authenticated and public contexts\n * @returns App configuration and loading state\n */\nexport function useAppConfig(): UseAppConfigReturn {\n // Check if we're in a public page context first\n const isPublicPage = useIsPublicPage();\n \n // Try to get appName from PublicPageContext (access context directly to avoid hook rule violations)\n const publicPageContext = useContext(PublicPageContext);\n const hasPublicContext = publicPageContext !== undefined;\n const contextAppName = publicPageContext?.appName || null;\n const getNodeEnvVar = (key: string): string | undefined =>\n typeof process !== 'undefined' && process.env ? process.env[key] : undefined;\n \n if (isPublicPage) {\n const getAppName = (): string => {\n // When tests mock public mode without the provider, default to PACE\n if (!hasPublicContext) {\n return 'PACE';\n }\n \n // Priority 1: Use appName from PublicPageContext (passed from App.tsx)\n if (contextAppName) {\n return contextAppName;\n }\n \n // Priority 2: Check environment variables\n if (typeof import.meta !== 'undefined' && (import.meta as any).env) {\n const env = (import.meta as any).env;\n return env.VITE_APP_NAME || \n env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.env.VITE_APP_NAME || \n import.meta.env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n \n const nodeEnvAppName =\n getNodeEnvVar('VITE_APP_NAME') ||\n getNodeEnvVar('NEXT_PUBLIC_APP_NAME');\n if (nodeEnvAppName) {\n return nodeEnvAppName;\n }\n \n // Priority 3: Default fallback\n return 'PACE';\n };\n \n return useMemo(() => ({\n appName: getAppName(),\n isLoading: false\n }), [contextAppName, hasPublicContext]);\n }\n \n // For authenticated pages, use UnifiedAuthProvider\n try {\n const { appName } = useUnifiedAuth();\n return useMemo(() => ({\n appName,\n isLoading: false\n }), [appName]);\n } catch (error) {\n // Fallback if UnifiedAuthProvider is not available\n return useMemo(() => ({\n appName: contextAppName ||\n getNodeEnvVar('VITE_APP_NAME') ||\n getNodeEnvVar('NEXT_PUBLIC_APP_NAME') ||\n 'PACE',\n isLoading: false\n }), [contextAppName]);\n }\n} ","/**\n * @file Organisation Security Hook\n * @package @jmruthers/pace-core\n * @module Hooks/OrganisationSecurity\n * @since 0.4.0\n *\n * Security-focused hook for organisation access validation and super admin functionality.\n * Provides utilities for validating user access to organisations and checking permissions.\n */\n\nimport { useCallback, useMemo, useEffect, useState } from 'react';\nimport { useUnifiedAuth } from '../providers';\nimport { useOrganisations } from './useOrganisations';\n// Legacy useRBAC hook removed - use new RBAC system instead\nimport type { OrganisationSecurityError, SuperAdminContext } from '../types/organisation';\nimport type { Permission } from '../rbac/types';\nimport { logger } from '../utils/core/logger';\n\n/**\n * Organisation security hook interface.\n * Provides security utilities for organisation access validation and super admin functionality.\n */\nexport interface OrganisationSecurityHook {\n // Super admin context\n superAdminContext: SuperAdminContext;\n \n // Access validation\n validateOrganisationAccess: (orgId: string) => Promise<boolean>;\n hasMinimumRole: (minRole: string, orgId?: string) => boolean;\n canAccessChildOrganisations: (orgId?: string) => boolean;\n \n // Permission checks\n hasPermission: (permission: string, orgId?: string) => Promise<boolean>;\n getUserPermissions: (orgId?: string) => Promise<string[]>;\n \n // Audit logging\n logOrganisationAccess: (action: string, details?: Record<string, unknown>) => Promise<void>;\n \n // Security utilities\n ensureOrganisationAccess: (orgId: string) => Promise<void>;\n validateUserAccess: (userId: string, orgId: string) => Promise<boolean>;\n}\n\nexport const useOrganisationSecurity = (): OrganisationSecurityHook => {\n const { user, session, supabase } = useUnifiedAuth();\n const { selectedOrganisation, getUserRole, validateOrganisationAccess: validateAccess } = useOrganisations();\n \n // Super admin status - query database for security (user_metadata can be spoofed)\n const [isSuperAdmin, setIsSuperAdmin] = useState<boolean>(false);\n const [isCheckingSuperAdmin, setIsCheckingSuperAdmin] = useState(false);\n\n // Check super admin status from database\n useEffect(() => {\n if (!user || !session || !supabase) {\n setIsSuperAdmin(false);\n return;\n }\n\n const checkSuperAdmin = async () => {\n setIsCheckingSuperAdmin(true);\n try {\n const now = new Date().toISOString();\n const { data, error } = await supabase\n .from('rbac_global_roles')\n .select('role')\n .eq('user_id', user.id)\n .eq('role', 'super_admin')\n .lte('valid_from', now)\n .or(`valid_to.is.null,valid_to.gte.${now}`)\n .limit(1);\n\n setIsSuperAdmin(!error && data && data.length > 0);\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Error checking super admin status:', error);\n setIsSuperAdmin(false);\n } finally {\n setIsCheckingSuperAdmin(false);\n }\n };\n\n checkSuperAdmin();\n }, [user, session, supabase]);\n\n // Super admin context\n const superAdminContext = useMemo((): SuperAdminContext => {\n return {\n isSuperAdmin,\n hasGlobalAccess: isSuperAdmin,\n canManageAllOrganisations: isSuperAdmin\n };\n }, [isSuperAdmin]);\n\n // Validate organisation access with database check\n const validateOrganisationAccess = useCallback(async (orgId: string): Promise<boolean> => {\n if (!user || !session || !supabase) return false;\n \n try {\n // Super admin has access to all organisations\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n // Check organisation membership using consolidated rbac_organisation_roles table\n const { data, error } = await supabase\n .from('rbac_organisation_roles')\n .select('id')\n .eq('user_id', user.id)\n .eq('organisation_id', orgId)\n .eq('status', 'active')\n .is('revoked_at', null)\n .in('role', ['org_admin', 'leader', 'member']) // Only actual members, not supporters\n .single();\n\n if (error) {\n logger.error('useOrganisationSecurity', 'Error validating organisation access:', error);\n return false;\n }\n\n return !!data;\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception validating organisation access:', error);\n return false;\n }\n }, [user, session, supabase, superAdminContext.isSuperAdmin]);\n\n // Check if user has minimum role\n const hasMinimumRole = useCallback((minRole: string, orgId?: string): boolean => {\n // Super admin has all roles\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId) return false;\n\n const userRole = getUserRole(targetOrgId);\n const roleHierarchy = ['supporter', 'member', 'leader', 'org_admin'];\n \n const userRoleIndex = roleHierarchy.indexOf(userRole);\n const minRoleIndex = roleHierarchy.indexOf(minRole);\n \n return userRoleIndex >= minRoleIndex;\n }, [selectedOrganisation, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Check if user can access child organisations\n const canAccessChildOrganisations = useCallback((orgId?: string): boolean => {\n // Super admin can access all organisations\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId) return false;\n\n const userRole = getUserRole(targetOrgId);\n return userRole === 'org_admin';\n }, [selectedOrganisation, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Check specific permission using the new RBAC system\n const hasPermission = useCallback(async (permission: string, orgId?: string): Promise<boolean> => {\n // Super admin has all permissions\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId || !user) return false;\n\n try {\n // Use the new RBAC system with caching\n const { isPermittedCached } = await import('../rbac/api');\n \n const scope = {\n organisationId: targetOrgId,\n eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,\n appId: user.user_metadata?.appId || user.app_metadata?.appId,\n };\n\n return await isPermittedCached({\n userId: user.id,\n scope,\n permission: permission as Permission\n });\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception checking permission:', error);\n return false;\n }\n }, [selectedOrganisation, user, superAdminContext.isSuperAdmin]);\n\n // Get user's permissions for organisation using the new RBAC system\n const getUserPermissions = useCallback(async (orgId?: string): Promise<string[]> => {\n // Super admin has all permissions\n if (superAdminContext.isSuperAdmin) {\n return ['*']; // All permissions\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId || !user) return [];\n\n try {\n // Use the new RBAC system\n const { getPermissionMap } = await import('../rbac/api');\n \n const scope = {\n organisationId: targetOrgId,\n eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,\n appId: user.user_metadata?.appId || user.app_metadata?.appId,\n };\n\n const permissionMap = await getPermissionMap({\n userId: user.id,\n scope\n });\n \n // Flatten all permissions from all pages\n const allPermissions = Object.entries(permissionMap)\n .filter(([, allowed]) => allowed)\n .map(([permission]) => permission);\n return [...new Set(allPermissions)]; // Remove duplicates\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception getting user permissions:', error);\n return [];\n }\n }, [selectedOrganisation, user, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Log organisation access for audit using the new RBAC audit system\n const logOrganisationAccess = useCallback(async (action: string, details?: Record<string, unknown>): Promise<void> => {\n if (!user || !selectedOrganisation) return;\n\n try {\n // Use the new RBAC audit system - only if we have a valid organisation ID\n if (selectedOrganisation.id) {\n const { emitAuditEvent } = await import('../rbac/audit');\n \n await emitAuditEvent({\n type: 'permission_check',\n userId: user.id,\n organisationId: selectedOrganisation.id,\n permission: action,\n decision: true, // Assume access was granted if we're logging it\n source: 'api',\n duration_ms: 0, // No actual permission check performed here\n metadata: details || {}\n });\n }\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Error logging organisation access:', error);\n }\n }, [user, selectedOrganisation]);\n\n // Ensure organisation access (throws if no access)\n const ensureOrganisationAccess = useCallback(async (orgId: string): Promise<void> => {\n const hasAccess = await validateOrganisationAccess(orgId);\n \n if (!hasAccess) {\n const error = new Error(`User does not have access to organisation ${orgId}`) as OrganisationSecurityError;\n error.name = 'OrganisationSecurityError';\n error.code = 'ACCESS_DENIED';\n error.organisationId = orgId;\n error.userId = user?.id;\n throw error;\n }\n }, [validateOrganisationAccess, user]);\n\n // Validate user access (for admin functions)\n const validateUserAccess = useCallback(async (userId: string, orgId: string): Promise<boolean> => {\n if (!supabase) return false;\n\n try {\n // Super admin can validate any user\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n // Regular users can only validate their own access\n if (userId !== user?.id) {\n return false;\n }\n\n return await validateOrganisationAccess(orgId);\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception validating user access:', error);\n return false;\n }\n }, [supabase, superAdminContext.isSuperAdmin, user, validateOrganisationAccess]);\n\n return {\n superAdminContext,\n validateOrganisationAccess,\n hasMinimumRole,\n canAccessChildOrganisations,\n hasPermission,\n getUserPermissions,\n logOrganisationAccess,\n ensureOrganisationAccess,\n validateUserAccess\n };\n}; "],"mappings":"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","names":["jsx","React","ErrorBoundary","createContext","useContext","useMemo","jsx","createContext","useMemo","ErrorBoundary","useContext","useMemo","useContext","useContext","useMemo","useMemo","useMemo"]}
|
|
@@ -5,20 +5,22 @@ import {
|
|
|
5
5
|
useMultiplePermissions,
|
|
6
6
|
useResolvedScope,
|
|
7
7
|
useSecureSupabase
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YDQHOZNA.js";
|
|
9
9
|
import {
|
|
10
10
|
useOrganisationSecurity
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-6Z7LTB3D.js";
|
|
12
12
|
import {
|
|
13
13
|
useUnifiedAuth
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-DWUBLJJM.js";
|
|
15
15
|
import {
|
|
16
16
|
RBACCache,
|
|
17
|
-
RBACNotInitializedError,
|
|
18
17
|
getRBACConfig,
|
|
19
18
|
getRBACLogger,
|
|
20
19
|
rbacCache
|
|
21
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-RWEBCB47.js";
|
|
21
|
+
import {
|
|
22
|
+
RBACNotInitializedError
|
|
23
|
+
} from "./chunk-QRPVRXYT.js";
|
|
22
24
|
import {
|
|
23
25
|
createLogger,
|
|
24
26
|
logger
|
|
@@ -176,6 +178,7 @@ var PagePermissionGuardComponent = ({
|
|
|
176
178
|
const instanceId = useMemo2(() => Math.random().toString(36).substr(2, 9), []);
|
|
177
179
|
const { user, selectedOrganisation, selectedEvent, supabase, appId: contextAppId, appName } = useUnifiedAuth();
|
|
178
180
|
const [hasChecked, setHasChecked] = useState2(false);
|
|
181
|
+
const hasLoggedSuperAdminRef = useRef(false);
|
|
179
182
|
const effectivePageId = useMemo2(() => {
|
|
180
183
|
return pageId || pageName;
|
|
181
184
|
}, [pageId, pageName]);
|
|
@@ -189,7 +192,7 @@ var PagePermissionGuardComponent = ({
|
|
|
189
192
|
const checkSuperAdmin = async () => {
|
|
190
193
|
const startTime = Date.now();
|
|
191
194
|
try {
|
|
192
|
-
const { isSuperAdmin: checkSuperAdmin2 } = await import("./api-
|
|
195
|
+
const { isSuperAdmin: checkSuperAdmin2 } = await import("./api-IAGWF3ZG.js");
|
|
193
196
|
const timeoutPromise = new Promise((_, reject) => {
|
|
194
197
|
setTimeout(() => reject(new Error("Super admin check timeout")), 1e4);
|
|
195
198
|
});
|
|
@@ -350,9 +353,10 @@ var PagePermissionGuardComponent = ({
|
|
|
350
353
|
const shouldShowContent = isPermissionCheckComplete && hasValidScopeForPagePermissions && hasValidUser && !checkError && effectiveCan;
|
|
351
354
|
const scopeKey = effectiveScope ? `${effectiveScope.organisationId}-${effectiveScope.eventId}-${effectiveScope.appId}` : "no-scope";
|
|
352
355
|
const permissionKey = `${scopeKey}-${can}-${isLoading}-${!!checkError}-${hasChecked}`;
|
|
356
|
+
const lastLogStateRef = useRef("");
|
|
353
357
|
useEffect2(() => {
|
|
354
358
|
if (false) {
|
|
355
|
-
|
|
359
|
+
const currentState = JSON.stringify({
|
|
356
360
|
pageName,
|
|
357
361
|
userId: user?.id,
|
|
358
362
|
isSuperAdmin,
|
|
@@ -361,10 +365,24 @@ var PagePermissionGuardComponent = ({
|
|
|
361
365
|
canIsLoading,
|
|
362
366
|
hasChecked,
|
|
363
367
|
hasValidUser,
|
|
364
|
-
effectiveCan
|
|
365
|
-
stableScope,
|
|
366
|
-
effectiveScope
|
|
368
|
+
effectiveCan
|
|
367
369
|
});
|
|
370
|
+
if (currentState !== lastLogStateRef.current) {
|
|
371
|
+
lastLogStateRef.current = currentState;
|
|
372
|
+
console.log("[PagePermissionGuard] Permission check state", {
|
|
373
|
+
pageName,
|
|
374
|
+
userId: user?.id,
|
|
375
|
+
isSuperAdmin,
|
|
376
|
+
isLoading,
|
|
377
|
+
scopeLoading,
|
|
378
|
+
canIsLoading,
|
|
379
|
+
hasChecked,
|
|
380
|
+
hasValidUser,
|
|
381
|
+
effectiveCan,
|
|
382
|
+
stableScope,
|
|
383
|
+
effectiveScope
|
|
384
|
+
});
|
|
385
|
+
}
|
|
368
386
|
}
|
|
369
387
|
}, [pageName, user?.id, isSuperAdmin, isLoading, scopeLoading, canIsLoading, hasChecked, hasValidUser, effectiveCan, stableScope, effectiveScope]);
|
|
370
388
|
useEffect2(() => {
|
|
@@ -385,12 +403,20 @@ var PagePermissionGuardComponent = ({
|
|
|
385
403
|
return () => clearTimeout(timeout);
|
|
386
404
|
}
|
|
387
405
|
}, [isLoading, isSuperAdmin, hasValidUser, pageName, user?.id, scopeLoading, canIsLoading, hasChecked, stableScope, effectiveScope, appName]);
|
|
406
|
+
useEffect2(() => {
|
|
407
|
+
if (isSuperAdmin === true && hasValidUser && !hasLoggedSuperAdminRef.current && false) {
|
|
408
|
+
hasLoggedSuperAdminRef.current = true;
|
|
409
|
+
console.log("[PagePermissionGuard] Super admin access granted - bypassing all checks", {
|
|
410
|
+
pageName,
|
|
411
|
+
userId: user?.id,
|
|
412
|
+
operation
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
if (isSuperAdmin !== true) {
|
|
416
|
+
hasLoggedSuperAdminRef.current = false;
|
|
417
|
+
}
|
|
418
|
+
}, [isSuperAdmin, hasValidUser, pageName, user?.id, operation]);
|
|
388
419
|
if (isSuperAdmin === true && hasValidUser) {
|
|
389
|
-
console.log("[PagePermissionGuard] Super admin access granted - bypassing all checks", {
|
|
390
|
-
pageName,
|
|
391
|
-
userId: user?.id,
|
|
392
|
-
operation
|
|
393
|
-
});
|
|
394
420
|
return /* @__PURE__ */ jsx2(Fragment, { children });
|
|
395
421
|
}
|
|
396
422
|
if (isLoading || !hasValidUser || !hasChecked || isSuperAdmin === null) {
|
|
@@ -1354,13 +1380,13 @@ function withPermissionGuard(config, handler) {
|
|
|
1354
1380
|
if (!userId || !organisationId) {
|
|
1355
1381
|
throw new Error("User context required for permission check");
|
|
1356
1382
|
}
|
|
1357
|
-
const { isPermitted: isPermitted2 } = await import("./api-
|
|
1383
|
+
const { isPermitted: isPermitted2 } = await import("./api-IAGWF3ZG.js");
|
|
1358
1384
|
const hasPermission2 = await isPermitted2({
|
|
1359
1385
|
userId,
|
|
1360
1386
|
scope: { organisationId, eventId, appId },
|
|
1361
1387
|
permission: config.permission,
|
|
1362
1388
|
pageId: config.pageId
|
|
1363
|
-
}
|
|
1389
|
+
});
|
|
1364
1390
|
if (!hasPermission2) {
|
|
1365
1391
|
throw new Error(`Permission denied: ${config.permission}`);
|
|
1366
1392
|
}
|
|
@@ -1377,7 +1403,7 @@ function withAccessLevelGuard(minLevel, handler) {
|
|
|
1377
1403
|
if (!userId || !organisationId) {
|
|
1378
1404
|
throw new Error("User context required for access level check");
|
|
1379
1405
|
}
|
|
1380
|
-
const { getAccessLevel: getAccessLevel2 } = await import("./api-
|
|
1406
|
+
const { getAccessLevel: getAccessLevel2 } = await import("./api-IAGWF3ZG.js");
|
|
1381
1407
|
const accessLevel = await getAccessLevel2({
|
|
1382
1408
|
userId,
|
|
1383
1409
|
scope: { organisationId, eventId, appId }
|
|
@@ -1402,11 +1428,11 @@ function withRoleGuard(config, handler) {
|
|
|
1402
1428
|
throw new Error("User context required for role check");
|
|
1403
1429
|
}
|
|
1404
1430
|
if (config.globalRoles && config.globalRoles.length > 0) {
|
|
1405
|
-
const { isSuperAdmin } = await import("./api-
|
|
1431
|
+
const { isSuperAdmin } = await import("./api-IAGWF3ZG.js");
|
|
1406
1432
|
const isSuper = await isSuperAdmin(userId);
|
|
1407
1433
|
if (isSuper) {
|
|
1408
1434
|
if (organisationId) {
|
|
1409
|
-
const { emitAuditEvent: emitAuditEvent2 } = await import("./audit-
|
|
1435
|
+
const { emitAuditEvent: emitAuditEvent2 } = await import("./audit-V53FV5AG.js");
|
|
1410
1436
|
await emitAuditEvent2({
|
|
1411
1437
|
type: "permission_check",
|
|
1412
1438
|
userId,
|
|
@@ -1428,21 +1454,21 @@ function withRoleGuard(config, handler) {
|
|
|
1428
1454
|
}
|
|
1429
1455
|
}
|
|
1430
1456
|
if (config.organisationRoles && config.organisationRoles.length > 0) {
|
|
1431
|
-
const { isOrganisationAdmin } = await import("./api-
|
|
1457
|
+
const { isOrganisationAdmin } = await import("./api-IAGWF3ZG.js");
|
|
1432
1458
|
const isOrgAdmin = await isOrganisationAdmin(userId, organisationId);
|
|
1433
1459
|
if (!isOrgAdmin && config.requireAll !== false) {
|
|
1434
1460
|
throw new Error(`Organisation admin role required`);
|
|
1435
1461
|
}
|
|
1436
1462
|
}
|
|
1437
1463
|
if (config.eventAppRoles && config.eventAppRoles.length > 0 && eventId && appId) {
|
|
1438
|
-
const { isEventAdmin } = await import("./api-
|
|
1464
|
+
const { isEventAdmin } = await import("./api-IAGWF3ZG.js");
|
|
1439
1465
|
const isEventAdminUser = await isEventAdmin(userId, { organisationId, eventId, appId });
|
|
1440
1466
|
if (!isEventAdminUser && config.requireAll !== false) {
|
|
1441
1467
|
throw new Error(`Event admin role required`);
|
|
1442
1468
|
}
|
|
1443
1469
|
}
|
|
1444
1470
|
if (organisationId) {
|
|
1445
|
-
const { emitAuditEvent: emitAuditEvent2 } = await import("./audit-
|
|
1471
|
+
const { emitAuditEvent: emitAuditEvent2 } = await import("./audit-V53FV5AG.js");
|
|
1446
1472
|
await emitAuditEvent2({
|
|
1447
1473
|
type: "permission_check",
|
|
1448
1474
|
userId,
|
|
@@ -1475,13 +1501,13 @@ function createRBACMiddleware(config) {
|
|
|
1475
1501
|
);
|
|
1476
1502
|
if (protectedRoute) {
|
|
1477
1503
|
try {
|
|
1478
|
-
const { isPermitted: isPermitted2 } = await import("./api-
|
|
1504
|
+
const { isPermitted: isPermitted2 } = await import("./api-IAGWF3ZG.js");
|
|
1479
1505
|
const hasPermission2 = await isPermitted2({
|
|
1480
1506
|
userId,
|
|
1481
1507
|
scope: { organisationId },
|
|
1482
1508
|
permission: protectedRoute.permission,
|
|
1483
1509
|
pageId: protectedRoute.pageId
|
|
1484
|
-
}
|
|
1510
|
+
});
|
|
1485
1511
|
if (!hasPermission2) {
|
|
1486
1512
|
return res.redirect(config.fallbackUrl || "/access-denied");
|
|
1487
1513
|
}
|
|
@@ -1502,7 +1528,7 @@ function createRBACExpressMiddleware(config) {
|
|
|
1502
1528
|
return res.status(401).json({ error: "User context required" });
|
|
1503
1529
|
}
|
|
1504
1530
|
try {
|
|
1505
|
-
const { isPermitted: isPermitted2 } = await import("./api-
|
|
1531
|
+
const { isPermitted: isPermitted2 } = await import("./api-IAGWF3ZG.js");
|
|
1506
1532
|
const hasPermission2 = await isPermitted2({
|
|
1507
1533
|
userId,
|
|
1508
1534
|
scope: { organisationId, eventId, appId },
|
|
@@ -2024,4 +2050,4 @@ export {
|
|
|
2024
2050
|
getDirectSupabaseAuthFixes,
|
|
2025
2051
|
getQuickFixes
|
|
2026
2052
|
};
|
|
2027
|
-
//# sourceMappingURL=chunk-
|
|
2053
|
+
//# sourceMappingURL=chunk-CNCQDFLN.js.map
|