@jmruthers/pace-core 0.5.191 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +372 -0
- package/cursor-rules/01-standards-compliance.mdc +275 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +341 -0
- package/cursor-rules/04-testing-standards.mdc +315 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +392 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
- package/cursor-rules/CHANGELOG.md +101 -0
- package/cursor-rules/README.md +191 -0
- package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
- package/dist/{DataTable-WKRZD47S.js → DataTable-DQ7RSOHE.js} +8 -7
- package/dist/{PublicPageProvider-ULXC_u6U.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +37 -156
- package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
- package/dist/{UnifiedAuthProvider-FTSG5XH7.js → UnifiedAuthProvider-ATAP5UTR.js} +3 -3
- package/dist/{api-IHKALJZD.js → api-N774RPUA.js} +2 -2
- package/dist/{chunk-6C4YBBJM.js → chunk-3QRJFVBR.js} +1 -1
- package/dist/chunk-3QRJFVBR.js.map +1 -0
- package/dist/{chunk-OETXORNB.js → chunk-3XTALGJF.js} +211 -136
- package/dist/chunk-3XTALGJF.js.map +1 -0
- package/dist/{chunk-6TQDD426.js → chunk-4N5C5XZU.js} +47 -228
- package/dist/chunk-4N5C5XZU.js.map +1 -0
- package/dist/{chunk-LOMZXPSN.js → chunk-4ZC4GX36.js} +47 -74
- package/dist/chunk-4ZC4GX36.js.map +1 -0
- package/dist/{chunk-6LTQQAT6.js → chunk-BYFSK72L.js} +357 -158
- package/dist/chunk-BYFSK72L.js.map +1 -0
- package/dist/{chunk-XYXSXPUK.js → chunk-EXUD6RNJ.js} +50 -10
- package/dist/chunk-EXUD6RNJ.js.map +1 -0
- package/dist/{chunk-VKB2CO4Z.js → chunk-GLK6VM3F.js} +244 -249
- package/dist/chunk-GLK6VM3F.js.map +1 -0
- package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-XNYQOL3Z.js → chunk-JBKQ3SAO.js} +9 -18
- package/dist/chunk-JBKQ3SAO.js.map +1 -0
- package/dist/{chunk-ROXMHMY2.js → chunk-KNC55RTG.js} +13 -3
- package/dist/{chunk-ROXMHMY2.js.map → chunk-KNC55RTG.js.map} +1 -1
- package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
- package/dist/{chunk-ULHIJK66.js → chunk-T33XF5ZC.js} +255 -140
- package/dist/chunk-T33XF5ZC.js.map +1 -0
- package/dist/{chunk-VRGWKHDB.js → chunk-XM25TVIE.js} +100 -33
- package/dist/chunk-XM25TVIE.js.map +1 -0
- package/dist/components.d.ts +4 -4
- package/dist/components.js +9 -9
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +20 -25
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +18 -21
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +2 -20
- package/dist/rbac/index.js +7 -9
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -3
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +2 -2
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +10 -10
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +24 -11
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +2 -2
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +60 -38
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +194 -209
- package/docs/getting-started/cursor-rules.md +262 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/MIGRATION_GUIDE.md +4 -4
- package/docs/migration/REACT_19_MIGRATION.md +227 -0
- package/docs/migration/database-changes-december-2025.md +2 -1
- package/docs/rbac/event-based-apps.md +124 -6
- package/docs/standards/README.md +39 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +11 -6
- package/scripts/audit-consuming-app.cjs +961 -0
- package/scripts/check-pace-core-compliance.cjs +315 -61
- package/scripts/install-cursor-rules.cjs +236 -0
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/__tests__/rls-policies.test.ts +3 -1
- package/src/components/Badge/Badge.tsx +2 -4
- package/src/components/Button/Button.tsx +5 -4
- package/src/components/Calendar/Calendar.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
- package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +88 -16
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/DataTableCore.tsx +4 -7
- package/src/components/DataTable/components/DataTableModals.tsx +1 -1
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +86 -17
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +6 -5
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -3
- package/src/components/FileDisplay/FileDisplay.tsx +16 -4
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +30 -26
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.tsx +28 -30
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +8 -8
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -11
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -1
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +75 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +98 -69
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -8
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
- package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
- package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
- package/src/components/Select/Select.tsx +33 -22
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/UserMenu/UserMenu.tsx +1 -1
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +14 -7
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/public/usePublicEvent.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/services/useAuthService.ts +21 -3
- package/src/hooks/services/useEventService.ts +21 -3
- package/src/hooks/services/useInactivityService.ts +21 -3
- package/src/hooks/services/useOrganisationService.ts +21 -3
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useFileDisplay.ts +10 -17
- package/src/hooks/useFocusManagement.ts +2 -2
- package/src/hooks/useFocusTrap.ts +4 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +1 -1
- package/src/hooks/usePermissionCache.ts +1 -1
- package/src/hooks/useSecureDataAccess.test.ts +16 -9
- package/src/hooks/useSecureDataAccess.ts +22 -6
- package/src/hooks/useToast.ts +2 -2
- package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/EventServiceProvider.tsx +0 -8
- package/src/providers/services/UnifiedAuthProvider.tsx +196 -46
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +34 -40
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
- package/src/rbac/adapters.tsx +3 -22
- package/src/rbac/api.test.ts +2 -2
- package/src/rbac/api.ts +7 -1
- package/src/rbac/components/EnhancedNavigationMenu.tsx +3 -16
- package/src/rbac/components/NavigationGuard.tsx +2 -11
- package/src/rbac/components/NavigationProvider.tsx +1 -2
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +46 -13
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.tsx +1 -2
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
- package/src/rbac/engine.ts +14 -2
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/usePermissions.ts +51 -11
- package/src/rbac/hooks/useRBAC.ts +3 -13
- package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
- package/src/rbac/hooks/useResolvedScope.ts +58 -33
- package/src/rbac/hooks/useSecureSupabase.ts +4 -9
- package/src/rbac/secureClient.ts +43 -0
- package/src/services/EventService.ts +4 -57
- package/src/services/InactivityService.ts +127 -34
- package/src/services/OrganisationService.ts +68 -10
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-6LTQQAT6.js.map +0 -1
- package/dist/chunk-6TQDD426.js.map +0 -1
- package/dist/chunk-LOMZXPSN.js.map +0 -1
- package/dist/chunk-OETXORNB.js.map +0 -1
- package/dist/chunk-ULHIJK66.js.map +0 -1
- package/dist/chunk-VKB2CO4Z.js.map +0 -1
- package/dist/chunk-VRGWKHDB.js.map +0 -1
- package/dist/chunk-XNYQOL3Z.js.map +0 -1
- package/dist/chunk-XYXSXPUK.js.map +0 -1
- package/scripts/check-pace-core-compliance.js +0 -512
- package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
- package/src/utils/context/superAdminOverride.ts +0 -58
- /package/dist/{DataTable-WKRZD47S.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-FTSG5XH7.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
- /package/dist/{api-IHKALJZD.js.map → api-N774RPUA.js.map} +0 -0
- /package/dist/{chunk-QWWZ5CAQ.js.map → chunk-LXQLPRQ2.js.map} +0 -0
- /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
|
@@ -1,512 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Static Analysis Script for pace-core Compliance
|
|
5
|
-
* @package @jmruthers/pace-core
|
|
6
|
-
* @module Scripts/check-pace-core-compliance
|
|
7
|
-
*
|
|
8
|
-
* Scans a consuming app's codebase to check compliance with pace-core usage.
|
|
9
|
-
* Generates a report of violations and suggestions.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
|
|
15
|
-
// ANSI color codes for terminal output
|
|
16
|
-
const colors = {
|
|
17
|
-
reset: '\x1b[0m',
|
|
18
|
-
red: '\x1b[31m',
|
|
19
|
-
green: '\x1b[32m',
|
|
20
|
-
yellow: '\x1b[33m',
|
|
21
|
-
blue: '\x1b[34m',
|
|
22
|
-
cyan: '\x1b[36m',
|
|
23
|
-
bold: '\x1b[1m'
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// Load manifest
|
|
27
|
-
function loadManifest() {
|
|
28
|
-
const manifestPath = path.join(__dirname, '../core-usage-manifest.json');
|
|
29
|
-
if (!fs.existsSync(manifestPath)) {
|
|
30
|
-
console.error(`${colors.red}Error: core-usage-manifest.json not found at ${manifestPath}${colors.reset}`);
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Find project root (look for package.json, going up from current dir or script location)
|
|
37
|
-
function findProjectRoot(startDir = process.cwd()) {
|
|
38
|
-
let current = path.resolve(startDir);
|
|
39
|
-
while (current !== path.dirname(current)) {
|
|
40
|
-
if (fs.existsSync(path.join(current, 'package.json'))) {
|
|
41
|
-
return current;
|
|
42
|
-
}
|
|
43
|
-
current = path.dirname(current);
|
|
44
|
-
}
|
|
45
|
-
return startDir;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Scan file for violations
|
|
49
|
-
function scanFile(filePath, manifest) {
|
|
50
|
-
const violations = {
|
|
51
|
-
restrictedImports: [],
|
|
52
|
-
duplicateComponents: [],
|
|
53
|
-
duplicateHooks: [],
|
|
54
|
-
duplicateUtils: [],
|
|
55
|
-
suggestions: [],
|
|
56
|
-
customAuthCode: [],
|
|
57
|
-
duplicateConfig: [],
|
|
58
|
-
unprotectedPages: [],
|
|
59
|
-
directSupabaseAuth: []
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
63
|
-
const relativePath = path.relative(process.cwd(), filePath);
|
|
64
|
-
|
|
65
|
-
// Check for restricted imports
|
|
66
|
-
manifest.restrictedImports.forEach(({ module, reason }) => {
|
|
67
|
-
const importPattern = new RegExp(`from\\s+['"]${module.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]`, 'g');
|
|
68
|
-
if (importPattern.test(content)) {
|
|
69
|
-
violations.restrictedImports.push({
|
|
70
|
-
module,
|
|
71
|
-
reason,
|
|
72
|
-
file: relativePath,
|
|
73
|
-
line: getLineNumber(content, content.match(importPattern)[0])
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Also check for @radix-ui/* pattern
|
|
78
|
-
if (module.startsWith('@radix-ui/')) {
|
|
79
|
-
const radixPattern = /from\s+['"]@radix-ui\/[^'"]+['"]/g;
|
|
80
|
-
const matches = content.match(radixPattern);
|
|
81
|
-
if (matches) {
|
|
82
|
-
matches.forEach(match => {
|
|
83
|
-
const matchedModule = match.match(/['"]([^'"]+)['"]/)[1];
|
|
84
|
-
if (!manifest.restrictedImports.find(ri => ri.module === matchedModule)) {
|
|
85
|
-
violations.restrictedImports.push({
|
|
86
|
-
module: matchedModule,
|
|
87
|
-
reason: 'Use pace-core component instead of direct Radix UI import',
|
|
88
|
-
file: relativePath,
|
|
89
|
-
line: getLineNumber(content, match)
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Check for duplicate component names
|
|
98
|
-
const filename = path.basename(filePath);
|
|
99
|
-
const componentName = filename.replace(/\.(tsx?|jsx?)$/, '').replace(/\.(test|spec)$/, '');
|
|
100
|
-
|
|
101
|
-
if (manifest.components.includes(componentName)) {
|
|
102
|
-
// Check if this file exports a component
|
|
103
|
-
if (content.match(/export\s+(default\s+)?(function|const|class)\s+(\w+)?/)) {
|
|
104
|
-
violations.duplicateComponents.push({
|
|
105
|
-
component: componentName,
|
|
106
|
-
file: relativePath
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Check for duplicate hook names
|
|
112
|
-
if (filename.startsWith('use') && filename.endsWith('.ts') || filename.endsWith('.tsx')) {
|
|
113
|
-
const hookName = filename.replace(/\.(tsx?|jsx?)$/, '').replace(/\.(test|spec)$/, '');
|
|
114
|
-
if (manifest.hooks.includes(hookName)) {
|
|
115
|
-
if (content.match(/export\s+(default\s+)?(function|const)\s+(\w+)?/)) {
|
|
116
|
-
violations.duplicateHooks.push({
|
|
117
|
-
hook: hookName,
|
|
118
|
-
file: relativePath
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Check for duplicate util names
|
|
125
|
-
const utilName = filename.replace(/\.(ts|js)$/, '').replace(/\.(test|spec)$/, '');
|
|
126
|
-
if (manifest.utils.includes(utilName)) {
|
|
127
|
-
if (content.match(/export\s+(default\s+)?(function|const)\s+(\w+)?/)) {
|
|
128
|
-
violations.duplicateUtils.push({
|
|
129
|
-
util: utilName,
|
|
130
|
-
file: relativePath
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Check for native HTML elements that should use pace-core components
|
|
136
|
-
const nativeElementPatterns = {
|
|
137
|
-
'<button': { suggestion: 'Use Button from @jmruthers/pace-core' },
|
|
138
|
-
'<input': { suggestion: 'Use Input from @jmruthers/pace-core' },
|
|
139
|
-
'<textarea': { suggestion: 'Use Textarea from @jmruthers/pace-core' },
|
|
140
|
-
'<label': { suggestion: 'Use Label from @jmruthers/pace-core' }
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
Object.entries(nativeElementPatterns).forEach(([pattern, { suggestion }]) => {
|
|
144
|
-
if (content.includes(pattern) && !content.includes('from \'@jmruthers/pace-core\'')) {
|
|
145
|
-
violations.suggestions.push({
|
|
146
|
-
type: 'native-element',
|
|
147
|
-
suggestion,
|
|
148
|
-
file: relativePath,
|
|
149
|
-
pattern
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// ============================================
|
|
155
|
-
// RBAC/Auth Compliance Checks
|
|
156
|
-
// ============================================
|
|
157
|
-
|
|
158
|
-
// Check for custom auth/rbac/permission code that doesn't import from pace-core
|
|
159
|
-
const authRbacPatterns = [
|
|
160
|
-
// Custom auth hooks
|
|
161
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+useAuth\s*[=\(]/g, name: 'useAuth', type: 'hook' },
|
|
162
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+useLogin\s*[=\(]/g, name: 'useLogin', type: 'hook' },
|
|
163
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+useLogout\s*[=\(]/g, name: 'useLogout', type: 'hook' },
|
|
164
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+useSession\s*[=\(]/g, name: 'useSession', type: 'hook' },
|
|
165
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+useUser\s*[=\(]/g, name: 'useUser', type: 'hook' },
|
|
166
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+useAuthentication\s*[=\(]/g, name: 'useAuthentication', type: 'hook' },
|
|
167
|
-
// Custom RBAC hooks
|
|
168
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+usePermissions\s*[=\(]/g, name: 'usePermissions', type: 'hook' },
|
|
169
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+useCan\s*[=\(]/g, name: 'useCan', type: 'hook' },
|
|
170
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+useAccessLevel\s*[=\(]/g, name: 'useAccessLevel', type: 'hook' },
|
|
171
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+useRole\s*[=\(]/g, name: 'useRole', type: 'hook' },
|
|
172
|
-
// Custom RBAC components
|
|
173
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+PermissionGuard\s*[=\(]/g, name: 'PermissionGuard', type: 'component' },
|
|
174
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+AuthGuard\s*[=\(]/g, name: 'AuthGuard', type: 'component' },
|
|
175
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+RoleGuard\s*[=\(]/g, name: 'RoleGuard', type: 'component' },
|
|
176
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+AccessGuard\s*[=\(]/g, name: 'AccessGuard', type: 'component' },
|
|
177
|
-
// Custom permission utilities
|
|
178
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+checkPermission\s*[=\(]/g, name: 'checkPermission', type: 'util' },
|
|
179
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+hasPermission\s*[=\(]/g, name: 'hasPermission', type: 'util' },
|
|
180
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+hasAccess\s*[=\(]/g, name: 'hasAccess', type: 'util' },
|
|
181
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+canAccess\s*[=\(]/g, name: 'canAccess', type: 'util' },
|
|
182
|
-
{ pattern: /export\s+(default\s+)?(function|const)\s+isPermitted\s*[=\(]/g, name: 'isPermitted', type: 'util' }
|
|
183
|
-
];
|
|
184
|
-
|
|
185
|
-
// Check if file imports from pace-core for auth/rbac
|
|
186
|
-
const hasPaceCoreImport = /from\s+['"]@jmruthers\/pace-core/.test(content) ||
|
|
187
|
-
/from\s+['"]@jmruthers\/pace-core\/rbac/.test(content) ||
|
|
188
|
-
/from\s+['"]@jmruthers\/pace-core\/providers/.test(content);
|
|
189
|
-
|
|
190
|
-
authRbacPatterns.forEach(({ pattern, name, type }) => {
|
|
191
|
-
if (pattern.test(content) && !hasPaceCoreImport) {
|
|
192
|
-
violations.customAuthCode.push({
|
|
193
|
-
name,
|
|
194
|
-
type,
|
|
195
|
-
file: relativePath,
|
|
196
|
-
line: getLineNumber(content, content.match(pattern)[0]),
|
|
197
|
-
reason: `Custom ${type} '${name}' detected. Use pace-core's ${name} instead.`
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
// Check for duplicate Supabase client configurations
|
|
203
|
-
const supabaseCreateClientPattern = /createClient\s*\(/g;
|
|
204
|
-
const supabaseCreateClientMatches = content.match(supabaseCreateClientPattern);
|
|
205
|
-
if (supabaseCreateClientMatches && supabaseCreateClientMatches.length > 1) {
|
|
206
|
-
violations.duplicateConfig.push({
|
|
207
|
-
type: 'supabase-client',
|
|
208
|
-
file: relativePath,
|
|
209
|
-
count: supabaseCreateClientMatches.length,
|
|
210
|
-
reason: `Multiple Supabase client instantiations found (${supabaseCreateClientMatches.length}). Consolidate to a single client configuration.`
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Check for Supabase URL/key configuration in multiple places
|
|
215
|
-
const supabaseUrlPattern = /(SUPABASE_URL|VITE_SUPABASE_URL|NEXT_PUBLIC_SUPABASE_URL|REACT_APP_SUPABASE_URL)/g;
|
|
216
|
-
const supabaseKeyPattern = /(SUPABASE_ANON_KEY|VITE_SUPABASE_PUBLISHABLE_KEY|NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY|REACT_APP_SUPABASE_PUBLISHABLE_KEY)/g;
|
|
217
|
-
const urlMatches = content.match(supabaseUrlPattern);
|
|
218
|
-
const keyMatches = content.match(supabaseKeyPattern);
|
|
219
|
-
|
|
220
|
-
if ((urlMatches && urlMatches.length > 2) || (keyMatches && keyMatches.length > 2)) {
|
|
221
|
-
violations.duplicateConfig.push({
|
|
222
|
-
type: 'supabase-env',
|
|
223
|
-
file: relativePath,
|
|
224
|
-
reason: 'Supabase environment variables referenced multiple times. Consider centralizing configuration.'
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Check for unprotected pages/routes
|
|
229
|
-
// Look for route definitions without PagePermissionGuard
|
|
230
|
-
const routePatterns = [
|
|
231
|
-
/<Route\s+path=["'][^"']+["']/g,
|
|
232
|
-
/<Route\s+element\s*=/g,
|
|
233
|
-
/createBrowserRouter\s*\(/g,
|
|
234
|
-
/createRoutesFromElements/g
|
|
235
|
-
];
|
|
236
|
-
|
|
237
|
-
const isRouteFile = routePatterns.some(pattern => pattern.test(content));
|
|
238
|
-
const hasPagePermissionGuard = /PagePermissionGuard/.test(content) ||
|
|
239
|
-
/from\s+['"]@jmruthers\/pace-core\/rbac['"]/.test(content);
|
|
240
|
-
|
|
241
|
-
if (isRouteFile && !hasPagePermissionGuard && !relativePath.includes('test') && !relativePath.includes('spec')) {
|
|
242
|
-
violations.unprotectedPages.push({
|
|
243
|
-
file: relativePath,
|
|
244
|
-
reason: 'Route file found without PagePermissionGuard. All routes should be protected with PagePermissionGuard from pace-core.'
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Check for direct Supabase auth usage (should use UnifiedAuthProvider)
|
|
249
|
-
const directSupabaseAuthPatterns = [
|
|
250
|
-
/\.auth\.signIn/g,
|
|
251
|
-
/\.auth\.signUp/g,
|
|
252
|
-
/\.auth\.signOut/g,
|
|
253
|
-
/\.auth\.getSession/g,
|
|
254
|
-
/\.auth\.getUser/g,
|
|
255
|
-
/supabase\.auth\./g
|
|
256
|
-
];
|
|
257
|
-
|
|
258
|
-
const hasUnifiedAuthImport = /UnifiedAuthProvider/.test(content) ||
|
|
259
|
-
/useUnifiedAuth/.test(content) ||
|
|
260
|
-
/from\s+['"]@jmruthers\/pace-core\/providers/.test(content);
|
|
261
|
-
|
|
262
|
-
directSupabaseAuthPatterns.forEach(pattern => {
|
|
263
|
-
if (pattern.test(content) && !hasUnifiedAuthImport && !hasPaceCoreImport) {
|
|
264
|
-
violations.directSupabaseAuth.push({
|
|
265
|
-
file: relativePath,
|
|
266
|
-
line: getLineNumber(content, content.match(pattern)[0]),
|
|
267
|
-
reason: 'Direct Supabase auth usage detected. Use UnifiedAuthProvider and useUnifiedAuth from pace-core instead.'
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
return violations;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Get line number for a match
|
|
276
|
-
function getLineNumber(content, match) {
|
|
277
|
-
const lines = content.substring(0, content.indexOf(match)).split('\n');
|
|
278
|
-
return lines.length;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Generate report
|
|
282
|
-
function generateReport(allViolations, manifest) {
|
|
283
|
-
const totalRestricted = allViolations.restrictedImports.length;
|
|
284
|
-
const totalDuplicates =
|
|
285
|
-
allViolations.duplicateComponents.length +
|
|
286
|
-
allViolations.duplicateHooks.length +
|
|
287
|
-
allViolations.duplicateUtils.length;
|
|
288
|
-
const totalSuggestions = allViolations.suggestions.length;
|
|
289
|
-
const totalRbacAuth =
|
|
290
|
-
allViolations.customAuthCode.length +
|
|
291
|
-
allViolations.duplicateConfig.length +
|
|
292
|
-
allViolations.unprotectedPages.length +
|
|
293
|
-
allViolations.directSupabaseAuth.length;
|
|
294
|
-
const totalIssues = totalRestricted + totalDuplicates + totalSuggestions + totalRbacAuth;
|
|
295
|
-
|
|
296
|
-
console.log(`\n${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
|
|
297
|
-
console.log(`${colors.bold}${colors.cyan} pace-core Compliance Report${colors.reset}`);
|
|
298
|
-
console.log(`${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}\n`);
|
|
299
|
-
|
|
300
|
-
// Restricted Imports
|
|
301
|
-
if (totalRestricted > 0) {
|
|
302
|
-
console.log(`${colors.red}${colors.bold}❌ Restricted Imports Found: ${totalRestricted}${colors.reset}\n`);
|
|
303
|
-
allViolations.restrictedImports.forEach(({ module, reason, file, line }) => {
|
|
304
|
-
console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
|
|
305
|
-
console.log(` Import: ${colors.cyan}${module}${colors.reset}`);
|
|
306
|
-
console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
|
|
307
|
-
});
|
|
308
|
-
} else {
|
|
309
|
-
console.log(`${colors.green}✅ No restricted imports found${colors.reset}\n`);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Duplicate Components
|
|
313
|
-
if (allViolations.duplicateComponents.length > 0) {
|
|
314
|
-
console.log(`${colors.red}${colors.bold}❌ Duplicate Components Found: ${allViolations.duplicateComponents.length}${colors.reset}\n`);
|
|
315
|
-
allViolations.duplicateComponents.forEach(({ component, file }) => {
|
|
316
|
-
console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
|
|
317
|
-
console.log(` Component '${colors.cyan}${component}${colors.reset}' conflicts with pace-core component`);
|
|
318
|
-
console.log(` ${colors.yellow}Suggestion:${colors.reset} Use '${component}' from '@jmruthers/pace-core' instead\n`);
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Duplicate Hooks
|
|
323
|
-
if (allViolations.duplicateHooks.length > 0) {
|
|
324
|
-
console.log(`${colors.red}${colors.bold}❌ Duplicate Hooks Found: ${allViolations.duplicateHooks.length}${colors.reset}\n`);
|
|
325
|
-
allViolations.duplicateHooks.forEach(({ hook, file }) => {
|
|
326
|
-
console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
|
|
327
|
-
console.log(` Hook '${colors.cyan}${hook}${colors.reset}' conflicts with pace-core hook`);
|
|
328
|
-
console.log(` ${colors.yellow}Suggestion:${colors.reset} Use '${hook}' from '@jmruthers/pace-core' instead\n`);
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Duplicate Utils
|
|
333
|
-
if (allViolations.duplicateUtils.length > 0) {
|
|
334
|
-
console.log(`${colors.red}${colors.bold}❌ Duplicate Utils Found: ${allViolations.duplicateUtils.length}${colors.reset}\n`);
|
|
335
|
-
allViolations.duplicateUtils.forEach(({ util, file }) => {
|
|
336
|
-
console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
|
|
337
|
-
console.log(` Util '${colors.cyan}${util}${colors.reset}' conflicts with pace-core util`);
|
|
338
|
-
console.log(` ${colors.yellow}Suggestion:${colors.reset} Use '${util}' from '@jmruthers/pace-core' instead\n`);
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Suggestions
|
|
343
|
-
if (totalSuggestions > 0) {
|
|
344
|
-
console.log(`${colors.yellow}${colors.bold}💡 Suggestions: ${totalSuggestions}${colors.reset}\n`);
|
|
345
|
-
const grouped = {};
|
|
346
|
-
allViolations.suggestions.forEach(s => {
|
|
347
|
-
if (!grouped[s.file]) grouped[s.file] = [];
|
|
348
|
-
grouped[s.file].push(s);
|
|
349
|
-
});
|
|
350
|
-
Object.entries(grouped).forEach(([file, suggestions]) => {
|
|
351
|
-
console.log(` ${colors.yellow}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
|
|
352
|
-
suggestions.forEach(s => {
|
|
353
|
-
console.log(` ${s.suggestion}\n`);
|
|
354
|
-
});
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// RBAC/Auth Compliance Section
|
|
359
|
-
if (totalRbacAuth > 0) {
|
|
360
|
-
console.log(`\n${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
|
|
361
|
-
console.log(`${colors.bold}${colors.cyan} RBAC/Auth Compliance${colors.reset}`);
|
|
362
|
-
console.log(`${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}\n`);
|
|
363
|
-
|
|
364
|
-
// Custom Auth/RBAC Code
|
|
365
|
-
if (allViolations.customAuthCode.length > 0) {
|
|
366
|
-
console.log(`${colors.red}${colors.bold}❌ Custom Auth/RBAC Code Found: ${allViolations.customAuthCode.length}${colors.reset}\n`);
|
|
367
|
-
allViolations.customAuthCode.forEach(({ name, type, file, line, reason }) => {
|
|
368
|
-
console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
|
|
369
|
-
console.log(` ${type}: ${colors.cyan}${name}${colors.reset}`);
|
|
370
|
-
console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// Duplicate Configurations
|
|
375
|
-
if (allViolations.duplicateConfig.length > 0) {
|
|
376
|
-
console.log(`${colors.red}${colors.bold}❌ Duplicate Configurations Found: ${allViolations.duplicateConfig.length}${colors.reset}\n`);
|
|
377
|
-
allViolations.duplicateConfig.forEach(({ type, file, count, reason }) => {
|
|
378
|
-
console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
|
|
379
|
-
console.log(` Type: ${colors.cyan}${type}${colors.reset}${count ? ` (${count} instances)` : ''}`);
|
|
380
|
-
console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Unprotected Pages
|
|
385
|
-
if (allViolations.unprotectedPages.length > 0) {
|
|
386
|
-
console.log(`${colors.red}${colors.bold}❌ Unprotected Pages Found: ${allViolations.unprotectedPages.length}${colors.reset}\n`);
|
|
387
|
-
allViolations.unprotectedPages.forEach(({ file, reason }) => {
|
|
388
|
-
console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
|
|
389
|
-
console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Direct Supabase Auth Usage
|
|
394
|
-
if (allViolations.directSupabaseAuth.length > 0) {
|
|
395
|
-
console.log(`${colors.red}${colors.bold}❌ Direct Supabase Auth Usage Found: ${allViolations.directSupabaseAuth.length}${colors.reset}\n`);
|
|
396
|
-
allViolations.directSupabaseAuth.forEach(({ file, line, reason }) => {
|
|
397
|
-
console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
|
|
398
|
-
console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
} else {
|
|
402
|
-
console.log(`\n${colors.green}✅ RBAC/Auth compliance: All checks passed${colors.reset}\n`);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Summary
|
|
406
|
-
console.log(`${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
|
|
407
|
-
console.log(`${colors.bold}Summary:${colors.reset}`);
|
|
408
|
-
console.log(` Total Issues: ${totalIssues > 0 ? colors.red : colors.green}${totalIssues}${colors.reset}`);
|
|
409
|
-
console.log(` - Restricted Imports: ${totalRestricted > 0 ? colors.red : colors.green}${totalRestricted}${colors.reset}`);
|
|
410
|
-
console.log(` - Duplicate Components/Hooks/Utils: ${totalDuplicates > 0 ? colors.red : colors.green}${totalDuplicates}${colors.reset}`);
|
|
411
|
-
console.log(` - Suggestions: ${colors.yellow}${totalSuggestions}${colors.reset}`);
|
|
412
|
-
console.log(` - RBAC/Auth Issues: ${totalRbacAuth > 0 ? colors.red : colors.green}${totalRbacAuth}${colors.reset}`);
|
|
413
|
-
|
|
414
|
-
if (totalIssues === 0) {
|
|
415
|
-
console.log(`\n${colors.green}${colors.bold}✅ Excellent! Your codebase is fully compliant with pace-core standards.${colors.reset}\n`);
|
|
416
|
-
return 0;
|
|
417
|
-
} else {
|
|
418
|
-
console.log(`\n${colors.yellow}${colors.bold}⚠️ Please review the issues above and migrate to pace-core components/hooks/utils.${colors.reset}\n`);
|
|
419
|
-
return 1;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Recursively find source files
|
|
424
|
-
function findSourceFiles(dir, fileList = []) {
|
|
425
|
-
const ignoreDirs = ['node_modules', 'dist', 'build', '.next', 'coverage', '__tests__', '__mocks__'];
|
|
426
|
-
const ignoreFiles = /\.(test|spec)\.(ts|tsx|js|jsx)$/;
|
|
427
|
-
const sourceExtensions = /\.(ts|tsx|js|jsx)$/;
|
|
428
|
-
|
|
429
|
-
try {
|
|
430
|
-
const items = fs.readdirSync(dir);
|
|
431
|
-
|
|
432
|
-
items.forEach(item => {
|
|
433
|
-
const fullPath = path.join(dir, item);
|
|
434
|
-
const stat = fs.statSync(fullPath);
|
|
435
|
-
|
|
436
|
-
if (stat.isDirectory()) {
|
|
437
|
-
if (!ignoreDirs.includes(item) && !item.startsWith('.')) {
|
|
438
|
-
findSourceFiles(fullPath, fileList);
|
|
439
|
-
}
|
|
440
|
-
} else if (stat.isFile()) {
|
|
441
|
-
if (sourceExtensions.test(item) && !ignoreFiles.test(item)) {
|
|
442
|
-
fileList.push(fullPath);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
} catch (error) {
|
|
447
|
-
// Skip directories we can't read
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
return fileList;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// Main function
|
|
454
|
-
function main() {
|
|
455
|
-
const manifest = loadManifest();
|
|
456
|
-
const projectRoot = findProjectRoot();
|
|
457
|
-
|
|
458
|
-
console.log(`${colors.cyan}Scanning project at: ${projectRoot}${colors.reset}`);
|
|
459
|
-
|
|
460
|
-
// Find all TypeScript/JavaScript files (excluding node_modules, dist, etc.)
|
|
461
|
-
const files = findSourceFiles(projectRoot);
|
|
462
|
-
|
|
463
|
-
console.log(`Found ${files.length} files to scan...\n`);
|
|
464
|
-
|
|
465
|
-
// Scan all files
|
|
466
|
-
const allViolations = {
|
|
467
|
-
restrictedImports: [],
|
|
468
|
-
duplicateComponents: [],
|
|
469
|
-
duplicateHooks: [],
|
|
470
|
-
duplicateUtils: [],
|
|
471
|
-
suggestions: [],
|
|
472
|
-
customAuthCode: [],
|
|
473
|
-
duplicateConfig: [],
|
|
474
|
-
unprotectedPages: [],
|
|
475
|
-
directSupabaseAuth: []
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
files.forEach(file => {
|
|
479
|
-
try {
|
|
480
|
-
const violations = scanFile(file, manifest);
|
|
481
|
-
allViolations.restrictedImports.push(...violations.restrictedImports);
|
|
482
|
-
allViolations.duplicateComponents.push(...violations.duplicateComponents);
|
|
483
|
-
allViolations.duplicateHooks.push(...violations.duplicateHooks);
|
|
484
|
-
allViolations.duplicateUtils.push(...violations.duplicateUtils);
|
|
485
|
-
allViolations.suggestions.push(...violations.suggestions);
|
|
486
|
-
allViolations.customAuthCode.push(...violations.customAuthCode);
|
|
487
|
-
allViolations.duplicateConfig.push(...violations.duplicateConfig);
|
|
488
|
-
allViolations.unprotectedPages.push(...violations.unprotectedPages);
|
|
489
|
-
allViolations.directSupabaseAuth.push(...violations.directSupabaseAuth);
|
|
490
|
-
} catch (error) {
|
|
491
|
-
console.error(`${colors.red}Error scanning ${file}: ${error.message}${colors.reset}`);
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
// Generate and display report
|
|
496
|
-
const exitCode = generateReport(allViolations, manifest);
|
|
497
|
-
process.exit(exitCode);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// Run if called directly
|
|
501
|
-
if (require.main === module) {
|
|
502
|
-
try {
|
|
503
|
-
main();
|
|
504
|
-
} catch (error) {
|
|
505
|
-
console.error(`${colors.red}Error: ${error.message}${colors.reset}`);
|
|
506
|
-
console.error(error.stack);
|
|
507
|
-
process.exit(1);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
module.exports = { main, scanFile, generateReport };
|
|
512
|
-
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file useSuperAdminBypass
|
|
3
|
-
* @package @jmruthers/pace-core
|
|
4
|
-
*
|
|
5
|
-
* Detects whether the current user is a super admin, keeps the
|
|
6
|
-
* server session override flag in sync, and exposes a boolean
|
|
7
|
-
* that downstream hooks can use to bypass organisation scoping.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
11
|
-
import { useUnifiedAuth, type UnifiedAuthContextType } from '../../providers/services/UnifiedAuthProvider';
|
|
12
|
-
import { isSuperAdmin as fetchIsSuperAdmin } from '../api';
|
|
13
|
-
import { setSuperAdminOverrideFlag } from '../../utils/context/superAdminOverride';
|
|
14
|
-
import { createLogger } from '../../utils/core/logger';
|
|
15
|
-
|
|
16
|
-
const log = createLogger('useSuperAdminBypass');
|
|
17
|
-
|
|
18
|
-
export interface SuperAdminBypassState {
|
|
19
|
-
/** True when the user has been verified as a super admin */
|
|
20
|
-
isSuperAdmin: boolean;
|
|
21
|
-
/** True while the hook is checking the server */
|
|
22
|
-
isLoading: boolean;
|
|
23
|
-
/** Error returned by the verification request, if any */
|
|
24
|
-
error: Error | null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function useSafeUnifiedAuth(): UnifiedAuthContextType | null {
|
|
28
|
-
try {
|
|
29
|
-
return useUnifiedAuth();
|
|
30
|
-
} catch (error) {
|
|
31
|
-
log.debug('useSuperAdminBypass', 'UnifiedAuthProvider not available, falling back to defaults', {
|
|
32
|
-
error: error instanceof Error ? error.message : error
|
|
33
|
-
});
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function useSuperAdminBypass(): SuperAdminBypassState {
|
|
39
|
-
const authContext = useSafeUnifiedAuth();
|
|
40
|
-
const user = authContext?.user ?? null;
|
|
41
|
-
const supabase = authContext?.supabase ?? null;
|
|
42
|
-
const metadataHint =
|
|
43
|
-
Boolean(user?.app_metadata?.is_super_admin) ||
|
|
44
|
-
Boolean(user?.user_metadata?.is_super_admin);
|
|
45
|
-
|
|
46
|
-
const [isSuperAdminState, setIsSuperAdminState] = useState<boolean>(metadataHint);
|
|
47
|
-
const [hasVerified, setHasVerified] = useState<boolean>(!user?.id);
|
|
48
|
-
const [isLoading, setIsLoading] = useState<boolean>(!!user?.id);
|
|
49
|
-
const [error, setError] = useState<Error | null>(null);
|
|
50
|
-
const lastOverrideValueRef = useRef<boolean | null>(null);
|
|
51
|
-
|
|
52
|
-
// Verify against the RBAC engine whenever the user changes
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
if (!user?.id) {
|
|
55
|
-
setIsSuperAdminState(false);
|
|
56
|
-
setHasVerified(true);
|
|
57
|
-
setIsLoading(false);
|
|
58
|
-
setError(null);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
let cancelled = false;
|
|
63
|
-
setIsLoading(true);
|
|
64
|
-
setError(null);
|
|
65
|
-
|
|
66
|
-
fetchIsSuperAdmin(user.id)
|
|
67
|
-
.then((result) => {
|
|
68
|
-
if (cancelled) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
setIsSuperAdminState(result);
|
|
72
|
-
setHasVerified(true);
|
|
73
|
-
})
|
|
74
|
-
.catch((err) => {
|
|
75
|
-
if (cancelled) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
const normalisedError =
|
|
79
|
-
err instanceof Error ? err : new Error('Failed to resolve super admin status');
|
|
80
|
-
setError(normalisedError);
|
|
81
|
-
setIsSuperAdminState(false);
|
|
82
|
-
setHasVerified(false);
|
|
83
|
-
log.error('Unable to verify super admin status', normalisedError);
|
|
84
|
-
})
|
|
85
|
-
.finally(() => {
|
|
86
|
-
if (!cancelled) {
|
|
87
|
-
setIsLoading(false);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
return () => {
|
|
92
|
-
cancelled = true;
|
|
93
|
-
};
|
|
94
|
-
}, [user?.id]);
|
|
95
|
-
|
|
96
|
-
const shouldBypass = hasVerified && isSuperAdminState;
|
|
97
|
-
|
|
98
|
-
// Keep the database session flag in sync for auditing/RLS helpers
|
|
99
|
-
useEffect(() => {
|
|
100
|
-
if (!supabase) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (lastOverrideValueRef.current === shouldBypass) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
lastOverrideValueRef.current = shouldBypass;
|
|
107
|
-
|
|
108
|
-
setSuperAdminOverrideFlag({
|
|
109
|
-
supabase,
|
|
110
|
-
enabled: shouldBypass,
|
|
111
|
-
reason: 'pace-core-super-admin-bypass'
|
|
112
|
-
}).catch(() => {
|
|
113
|
-
// Errors are logged inside the helper
|
|
114
|
-
});
|
|
115
|
-
}, [supabase, shouldBypass]);
|
|
116
|
-
|
|
117
|
-
return useMemo(
|
|
118
|
-
() => ({
|
|
119
|
-
isSuperAdmin: shouldBypass,
|
|
120
|
-
isLoading,
|
|
121
|
-
error
|
|
122
|
-
}),
|
|
123
|
-
[shouldBypass, isLoading, error]
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Super Admin Override Utility
|
|
3
|
-
* @package @jmruthers/pace-core
|
|
4
|
-
* @module Utils/SuperAdminOverride
|
|
5
|
-
*
|
|
6
|
-
* Provides helpers for toggling the database session flag that
|
|
7
|
-
* signals a super admin override. This ensures SECURITY DEFINER
|
|
8
|
-
* functions and RLS policies can audit elevated operations.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
12
|
-
import { createLogger } from '../core/logger';
|
|
13
|
-
|
|
14
|
-
const log = createLogger('superAdminOverride');
|
|
15
|
-
|
|
16
|
-
interface SuperAdminOverrideParams {
|
|
17
|
-
supabase: SupabaseClient | null | undefined;
|
|
18
|
-
enabled: boolean;
|
|
19
|
-
reason?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Toggle the super admin override flag in the current Supabase session.
|
|
24
|
-
* Also records the action server-side for audit purposes.
|
|
25
|
-
*/
|
|
26
|
-
export async function setSuperAdminOverrideFlag({
|
|
27
|
-
supabase,
|
|
28
|
-
enabled,
|
|
29
|
-
reason = 'client_request'
|
|
30
|
-
}: SuperAdminOverrideParams): Promise<void> {
|
|
31
|
-
if (!supabase) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
const { error } = await supabase.rpc('set_super_admin_override', {
|
|
37
|
-
p_enabled: enabled,
|
|
38
|
-
p_reason: reason
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
if (error) {
|
|
42
|
-
log.error('Failed to toggle super admin override', {
|
|
43
|
-
enabled,
|
|
44
|
-
reason,
|
|
45
|
-
error: error.message
|
|
46
|
-
});
|
|
47
|
-
} else {
|
|
48
|
-
log.debug('Super admin override flag updated', { enabled, reason });
|
|
49
|
-
}
|
|
50
|
-
} catch (rpcError) {
|
|
51
|
-
log.error('Unexpected error toggling super admin override', {
|
|
52
|
-
enabled,
|
|
53
|
-
reason,
|
|
54
|
-
error: rpcError instanceof Error ? rpcError.message : rpcError
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|