@jmruthers/pace-core 0.5.191 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +372 -0
- package/cursor-rules/01-standards-compliance.mdc +275 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +341 -0
- package/cursor-rules/04-testing-standards.mdc +315 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +392 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
- package/cursor-rules/CHANGELOG.md +101 -0
- package/cursor-rules/README.md +191 -0
- package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
- package/dist/{DataTable-WKRZD47S.js → DataTable-DQ7RSOHE.js} +8 -7
- package/dist/{PublicPageProvider-ULXC_u6U.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +37 -156
- package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
- package/dist/{UnifiedAuthProvider-FTSG5XH7.js → UnifiedAuthProvider-ATAP5UTR.js} +3 -3
- package/dist/{api-IHKALJZD.js → api-N774RPUA.js} +2 -2
- package/dist/{chunk-6C4YBBJM.js → chunk-3QRJFVBR.js} +1 -1
- package/dist/chunk-3QRJFVBR.js.map +1 -0
- package/dist/{chunk-OETXORNB.js → chunk-3XTALGJF.js} +211 -136
- package/dist/chunk-3XTALGJF.js.map +1 -0
- package/dist/{chunk-6TQDD426.js → chunk-4N5C5XZU.js} +47 -228
- package/dist/chunk-4N5C5XZU.js.map +1 -0
- package/dist/{chunk-LOMZXPSN.js → chunk-4ZC4GX36.js} +47 -74
- package/dist/chunk-4ZC4GX36.js.map +1 -0
- package/dist/{chunk-6LTQQAT6.js → chunk-BYFSK72L.js} +357 -158
- package/dist/chunk-BYFSK72L.js.map +1 -0
- package/dist/{chunk-XYXSXPUK.js → chunk-EXUD6RNJ.js} +50 -10
- package/dist/chunk-EXUD6RNJ.js.map +1 -0
- package/dist/{chunk-VKB2CO4Z.js → chunk-GLK6VM3F.js} +244 -249
- package/dist/chunk-GLK6VM3F.js.map +1 -0
- package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-XNYQOL3Z.js → chunk-JBKQ3SAO.js} +9 -18
- package/dist/chunk-JBKQ3SAO.js.map +1 -0
- package/dist/{chunk-ROXMHMY2.js → chunk-KNC55RTG.js} +13 -3
- package/dist/{chunk-ROXMHMY2.js.map → chunk-KNC55RTG.js.map} +1 -1
- package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
- package/dist/{chunk-ULHIJK66.js → chunk-T33XF5ZC.js} +255 -140
- package/dist/chunk-T33XF5ZC.js.map +1 -0
- package/dist/{chunk-VRGWKHDB.js → chunk-XM25TVIE.js} +100 -33
- package/dist/chunk-XM25TVIE.js.map +1 -0
- package/dist/components.d.ts +4 -4
- package/dist/components.js +9 -9
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +20 -25
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +18 -21
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +2 -20
- package/dist/rbac/index.js +7 -9
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -3
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +2 -2
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +10 -10
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +24 -11
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +2 -2
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +60 -38
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +194 -209
- package/docs/getting-started/cursor-rules.md +262 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/MIGRATION_GUIDE.md +4 -4
- package/docs/migration/REACT_19_MIGRATION.md +227 -0
- package/docs/migration/database-changes-december-2025.md +2 -1
- package/docs/rbac/event-based-apps.md +124 -6
- package/docs/standards/README.md +39 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +11 -6
- package/scripts/audit-consuming-app.cjs +961 -0
- package/scripts/check-pace-core-compliance.cjs +315 -61
- package/scripts/install-cursor-rules.cjs +236 -0
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/__tests__/rls-policies.test.ts +3 -1
- package/src/components/Badge/Badge.tsx +2 -4
- package/src/components/Button/Button.tsx +5 -4
- package/src/components/Calendar/Calendar.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
- package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +88 -16
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/DataTableCore.tsx +4 -7
- package/src/components/DataTable/components/DataTableModals.tsx +1 -1
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +86 -17
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +6 -5
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -3
- package/src/components/FileDisplay/FileDisplay.tsx +16 -4
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +30 -26
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.tsx +28 -30
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +8 -8
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -11
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -1
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +75 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +98 -69
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -8
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
- package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
- package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
- package/src/components/Select/Select.tsx +33 -22
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/UserMenu/UserMenu.tsx +1 -1
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +14 -7
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/public/usePublicEvent.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/services/useAuthService.ts +21 -3
- package/src/hooks/services/useEventService.ts +21 -3
- package/src/hooks/services/useInactivityService.ts +21 -3
- package/src/hooks/services/useOrganisationService.ts +21 -3
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useFileDisplay.ts +10 -17
- package/src/hooks/useFocusManagement.ts +2 -2
- package/src/hooks/useFocusTrap.ts +4 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +1 -1
- package/src/hooks/usePermissionCache.ts +1 -1
- package/src/hooks/useSecureDataAccess.test.ts +16 -9
- package/src/hooks/useSecureDataAccess.ts +22 -6
- package/src/hooks/useToast.ts +2 -2
- package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/EventServiceProvider.tsx +0 -8
- package/src/providers/services/UnifiedAuthProvider.tsx +196 -46
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +34 -40
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
- package/src/rbac/adapters.tsx +3 -22
- package/src/rbac/api.test.ts +2 -2
- package/src/rbac/api.ts +7 -1
- package/src/rbac/components/EnhancedNavigationMenu.tsx +3 -16
- package/src/rbac/components/NavigationGuard.tsx +2 -11
- package/src/rbac/components/NavigationProvider.tsx +1 -2
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +46 -13
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.tsx +1 -2
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
- package/src/rbac/engine.ts +14 -2
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/usePermissions.ts +51 -11
- package/src/rbac/hooks/useRBAC.ts +3 -13
- package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
- package/src/rbac/hooks/useResolvedScope.ts +58 -33
- package/src/rbac/hooks/useSecureSupabase.ts +4 -9
- package/src/rbac/secureClient.ts +43 -0
- package/src/services/EventService.ts +4 -57
- package/src/services/InactivityService.ts +127 -34
- package/src/services/OrganisationService.ts +68 -10
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-6LTQQAT6.js.map +0 -1
- package/dist/chunk-6TQDD426.js.map +0 -1
- package/dist/chunk-LOMZXPSN.js.map +0 -1
- package/dist/chunk-OETXORNB.js.map +0 -1
- package/dist/chunk-ULHIJK66.js.map +0 -1
- package/dist/chunk-VKB2CO4Z.js.map +0 -1
- package/dist/chunk-VRGWKHDB.js.map +0 -1
- package/dist/chunk-XNYQOL3Z.js.map +0 -1
- package/dist/chunk-XYXSXPUK.js.map +0 -1
- package/scripts/check-pace-core-compliance.js +0 -512
- package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
- package/src/utils/context/superAdminOverride.ts +0 -58
- /package/dist/{DataTable-WKRZD47S.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-FTSG5XH7.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
- /package/dist/{api-IHKALJZD.js.map → api-N774RPUA.js.map} +0 -0
- /package/dist/{chunk-QWWZ5CAQ.js.map → chunk-LXQLPRQ2.js.map} +0 -0
- /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Define standard folder structure and file organization for consuming apps
|
|
3
|
+
globs: ["**/*.{ts,tsx,js,jsx,md}"]
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
paceCoreVersion: "0.5.x"
|
|
6
|
+
rulesVersion: "2025-01-15"
|
|
7
|
+
---
|
|
8
|
+
# Project Structure Standard
|
|
9
|
+
|
|
10
|
+
This guide defines the standard folder structure and file organization for consuming apps in the PACE suite.
|
|
11
|
+
|
|
12
|
+
## MUST: Follow Standard Directory Structure
|
|
13
|
+
|
|
14
|
+
**Consuming apps MUST follow this structure:**
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
your-app/
|
|
18
|
+
├── .cursor/
|
|
19
|
+
│ └── rules/ # Cursor rules (pace-core rules + local rules)
|
|
20
|
+
├── src/
|
|
21
|
+
│ ├── components/ # App-specific components
|
|
22
|
+
│ ├── hooks/ # App-specific hooks
|
|
23
|
+
│ ├── services/ # App-specific services
|
|
24
|
+
│ ├── pages/ # Page components
|
|
25
|
+
│ ├── types/ # TypeScript type definitions
|
|
26
|
+
│ ├── utils/ # App-specific utilities
|
|
27
|
+
│ ├── App.tsx # Main app component
|
|
28
|
+
│ └── main.tsx # Entry point
|
|
29
|
+
├── supabase/
|
|
30
|
+
│ ├── migrations/ # Database migrations
|
|
31
|
+
│ └── functions/ # Edge functions (if used)
|
|
32
|
+
├── public/ # Static assets
|
|
33
|
+
├── tests/ # Integration/E2E tests (optional)
|
|
34
|
+
├── audit/ # Audit reports (generated)
|
|
35
|
+
├── package.json
|
|
36
|
+
├── tsconfig.json
|
|
37
|
+
├── vite.config.ts
|
|
38
|
+
└── vitest.config.ts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## MUST: Organize Components by Feature
|
|
42
|
+
|
|
43
|
+
**Components SHOULD be organized by feature/domain, not by type:**
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
src/
|
|
47
|
+
├── components/
|
|
48
|
+
│ ├── events/ # Event-related components
|
|
49
|
+
│ │ ├── EventCard.tsx
|
|
50
|
+
│ │ └── EventList.tsx
|
|
51
|
+
│ ├── users/ # User-related components
|
|
52
|
+
│ │ ├── UserProfile.tsx
|
|
53
|
+
│ │ └── UserList.tsx
|
|
54
|
+
│ └── shared/ # Shared app-specific components
|
|
55
|
+
│ ├── Layout.tsx
|
|
56
|
+
│ └── Navigation.tsx
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**MUST NOT:**
|
|
60
|
+
- Organize by component type (`buttons/`, `inputs/`, etc.) - use pace-core instead
|
|
61
|
+
- Create duplicate components that pace-core provides
|
|
62
|
+
|
|
63
|
+
## MUST: Colocate Tests
|
|
64
|
+
|
|
65
|
+
**Tests MUST be colocated with source files:**
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
src/
|
|
69
|
+
├── components/
|
|
70
|
+
│ └── EventCard/
|
|
71
|
+
│ ├── EventCard.tsx
|
|
72
|
+
│ └── EventCard.test.tsx
|
|
73
|
+
├── hooks/
|
|
74
|
+
│ ├── useEventData.ts
|
|
75
|
+
│ └── useEventData.test.ts
|
|
76
|
+
└── utils/
|
|
77
|
+
├── formatEvent.ts
|
|
78
|
+
└── formatEvent.test.ts
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## MUST: Follow Naming Conventions
|
|
82
|
+
|
|
83
|
+
### Files
|
|
84
|
+
- Components: `PascalCase.tsx` (e.g., `EventCard.tsx`)
|
|
85
|
+
- Hooks: `camelCase.ts` with `use` prefix (e.g., `useEventData.ts`)
|
|
86
|
+
- Utilities: `camelCase.ts` (e.g., `formatEvent.ts`)
|
|
87
|
+
- Types: `camelCase.ts` or `types.ts` (e.g., `eventTypes.ts`)
|
|
88
|
+
- Tests: `*.test.ts` or `*.test.tsx`
|
|
89
|
+
- Config: `kebab-case.config.js` (e.g., `vite.config.ts`)
|
|
90
|
+
|
|
91
|
+
### Directories
|
|
92
|
+
- Use `kebab-case` for directories (e.g., `event-management/`)
|
|
93
|
+
- Match feature/domain names
|
|
94
|
+
|
|
95
|
+
## MUST: Place Migrations Correctly
|
|
96
|
+
|
|
97
|
+
**Database migrations MUST be in `supabase/migrations/`:**
|
|
98
|
+
- Format: `YYYYMMDDHHMMSS_description.sql`
|
|
99
|
+
- Example: `20250115143022_add_user_preferences.sql`
|
|
100
|
+
|
|
101
|
+
## SHOULD: Organize by Domain
|
|
102
|
+
|
|
103
|
+
**For larger apps, SHOULD organize by domain/feature:**
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
src/
|
|
107
|
+
├── domains/
|
|
108
|
+
│ ├── events/
|
|
109
|
+
│ │ ├── components/
|
|
110
|
+
│ │ ├── hooks/
|
|
111
|
+
│ │ ├── services/
|
|
112
|
+
│ │ └── types.ts
|
|
113
|
+
│ └── users/
|
|
114
|
+
│ ├── components/
|
|
115
|
+
│ ├── hooks/
|
|
116
|
+
│ ├── services/
|
|
117
|
+
│ └── types.ts
|
|
118
|
+
├── shared/ # Shared across domains
|
|
119
|
+
│ ├── components/
|
|
120
|
+
│ ├── hooks/
|
|
121
|
+
│ └── utils/
|
|
122
|
+
└── App.tsx
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## MUST: Keep Root Directory Clean
|
|
126
|
+
|
|
127
|
+
**Root directory SHOULD only contain:**
|
|
128
|
+
- Configuration files (`package.json`, `tsconfig.json`, etc.)
|
|
129
|
+
- Documentation (`README.md`, `CHANGELOG.md`)
|
|
130
|
+
- Build outputs (`.gitignore` them)
|
|
131
|
+
- `.cursor/` directory
|
|
132
|
+
|
|
133
|
+
**MUST NOT:**
|
|
134
|
+
- Place source files in root
|
|
135
|
+
- Place test files in root
|
|
136
|
+
- Place component files in root
|
|
137
|
+
|
|
138
|
+
## MUST: Use Consistent Import Paths
|
|
139
|
+
|
|
140
|
+
**MUST use consistent import patterns:**
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
// ✅ CORRECT - Absolute imports from src
|
|
144
|
+
import { EventCard } from '@/components/events/EventCard';
|
|
145
|
+
import { useEventData } from '@/hooks/useEventData';
|
|
146
|
+
import { formatEvent } from '@/utils/formatEvent';
|
|
147
|
+
|
|
148
|
+
// ✅ CORRECT - pace-core imports
|
|
149
|
+
import { Button, Card } from '@jmruthers/pace-core';
|
|
150
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core';
|
|
151
|
+
|
|
152
|
+
// ❌ WRONG - Relative imports for distant files
|
|
153
|
+
import { EventCard } from '../../../components/events/EventCard';
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Configure path aliases in `tsconfig.json`:**
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"compilerOptions": {
|
|
160
|
+
"paths": {
|
|
161
|
+
"@/*": ["./src/*"]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## MUST: Separate App Code from pace-core
|
|
168
|
+
|
|
169
|
+
**MUST NOT:**
|
|
170
|
+
- Modify pace-core code directly
|
|
171
|
+
- Create components that duplicate pace-core functionality
|
|
172
|
+
- Import from pace-core source (use published package)
|
|
173
|
+
|
|
174
|
+
**MUST:**
|
|
175
|
+
- Use pace-core as a dependency
|
|
176
|
+
- Import from `@jmruthers/pace-core`
|
|
177
|
+
- Create app-specific components only when pace-core doesn't provide them
|
|
178
|
+
|
|
179
|
+
## SHOULD: Document Structure Decisions
|
|
180
|
+
|
|
181
|
+
**For non-standard structures, SHOULD document why:**
|
|
182
|
+
- Add `STRUCTURE.md` or section in `README.md`
|
|
183
|
+
- Explain deviations from standard
|
|
184
|
+
- Justify custom organization
|
|
185
|
+
|
|
186
|
+
## File Organization Checklist
|
|
187
|
+
|
|
188
|
+
Before committing, verify:
|
|
189
|
+
- [ ] Components organized by feature, not type
|
|
190
|
+
- [ ] Tests colocated with source files
|
|
191
|
+
- [ ] Naming conventions followed
|
|
192
|
+
- [ ] Migrations in `supabase/migrations/`
|
|
193
|
+
- [ ] Root directory clean
|
|
194
|
+
- [ ] Import paths consistent
|
|
195
|
+
- [ ] No pace-core code modifications
|
|
196
|
+
- [ ] Structure documented if non-standard
|
|
197
|
+
|
|
198
|
+
## Reference
|
|
199
|
+
|
|
200
|
+
See `project-structure.mdc` in pace-core for detailed structure patterns.
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce SOLID architecture principles in consuming apps
|
|
3
|
+
globs: ["src/**/*.{ts,tsx}"]
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
paceCoreVersion: "0.5.x"
|
|
6
|
+
rulesVersion: "2025-01-15"
|
|
7
|
+
---
|
|
8
|
+
# SOLID Principles Guide
|
|
9
|
+
|
|
10
|
+
This guide enforces SOLID architecture principles to ensure maintainable, extensible, and testable code.
|
|
11
|
+
|
|
12
|
+
## Single Responsibility Principle (SRP)
|
|
13
|
+
|
|
14
|
+
**A class or function SHOULD have only one reason to change.**
|
|
15
|
+
|
|
16
|
+
### MUST: Single Responsibility
|
|
17
|
+
|
|
18
|
+
**Each function/component SHOULD do one thing:**
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
// ❌ WRONG - Multiple responsibilities
|
|
22
|
+
function UserProfile({ userId }: { userId: string }) {
|
|
23
|
+
const [user, setUser] = useState(null);
|
|
24
|
+
const [events, setEvents] = useState([]);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
// Fetching user
|
|
28
|
+
fetchUser(userId).then(setUser);
|
|
29
|
+
// Fetching events
|
|
30
|
+
fetchEvents(userId).then(setEvents);
|
|
31
|
+
// Formatting data
|
|
32
|
+
const formatted = formatUserData(user);
|
|
33
|
+
// Rendering UI
|
|
34
|
+
return <div>...</div>;
|
|
35
|
+
}, [userId]);
|
|
36
|
+
|
|
37
|
+
return <div>...</div>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ✅ CORRECT - Separated responsibilities
|
|
41
|
+
function UserProfile({ userId }: { userId: string }) {
|
|
42
|
+
const user = useUser(userId);
|
|
43
|
+
const events = useUserEvents(userId);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div>
|
|
47
|
+
<UserInfo user={user} />
|
|
48
|
+
<UserEvents events={events} />
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### MUST: Extract Complex Logic
|
|
55
|
+
|
|
56
|
+
**Complex logic SHOULD be extracted into hooks, services, or utilities:**
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
// ❌ WRONG - Business logic in component
|
|
60
|
+
function EventList() {
|
|
61
|
+
const [events, setEvents] = useState([]);
|
|
62
|
+
const [filtered, setFiltered] = useState([]);
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
fetchEvents().then(setEvents);
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
const filtered = events
|
|
70
|
+
.filter(e => e.status === 'active')
|
|
71
|
+
.sort((a, b) => a.date - b.date)
|
|
72
|
+
.map(e => ({ ...e, formattedDate: formatDate(e.date) }));
|
|
73
|
+
setFiltered(filtered);
|
|
74
|
+
}, [events]);
|
|
75
|
+
|
|
76
|
+
return <div>{filtered.map(...)}</div>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ✅ CORRECT - Logic in hook
|
|
80
|
+
function useFilteredEvents() {
|
|
81
|
+
const events = useEvents();
|
|
82
|
+
return useMemo(() => {
|
|
83
|
+
return events
|
|
84
|
+
.filter(e => e.status === 'active')
|
|
85
|
+
.sort((a, b) => a.date - b.date)
|
|
86
|
+
.map(e => ({ ...e, formattedDate: formatDate(e.date) }));
|
|
87
|
+
}, [events]);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function EventList() {
|
|
91
|
+
const filteredEvents = useFilteredEvents();
|
|
92
|
+
return <div>{filteredEvents.map(...)}</div>;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Open/Closed Principle (OCP)
|
|
97
|
+
|
|
98
|
+
**Software entities SHOULD be open for extension but closed for modification.**
|
|
99
|
+
|
|
100
|
+
### SHOULD: Use Composition Over Modification
|
|
101
|
+
|
|
102
|
+
**Extend functionality through composition, not modification:**
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// ❌ WRONG - Modifying base component
|
|
106
|
+
function BaseButton({ onClick, ...props }) {
|
|
107
|
+
return <button onClick={onClick} {...props} />;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function SpecialButton({ onClick, ...props }) {
|
|
111
|
+
// Modifying base behavior
|
|
112
|
+
const handleClick = (e) => {
|
|
113
|
+
trackEvent('button-click');
|
|
114
|
+
onClick?.(e);
|
|
115
|
+
};
|
|
116
|
+
return <BaseButton onClick={handleClick} {...props} />;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ✅ CORRECT - Composition with pace-core
|
|
120
|
+
import { Button } from '@jmruthers/pace-core';
|
|
121
|
+
|
|
122
|
+
function SpecialButton({ onClick, ...props }) {
|
|
123
|
+
const handleClick = (e) => {
|
|
124
|
+
trackEvent('button-click');
|
|
125
|
+
onClick?.(e);
|
|
126
|
+
};
|
|
127
|
+
return <Button onClick={handleClick} {...props} />;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### SHOULD: Use Configuration Over Code Changes
|
|
132
|
+
|
|
133
|
+
**Extend behavior through configuration:**
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
// ✅ CORRECT - Configurable behavior
|
|
137
|
+
interface DataTableConfig {
|
|
138
|
+
columns: Column[];
|
|
139
|
+
features: FeatureConfig;
|
|
140
|
+
rbac: RBACConfig;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function MyDataTable({ config }: { config: DataTableConfig }) {
|
|
144
|
+
return <DataTable {...config} />;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Liskov Substitution Principle (LSP)
|
|
149
|
+
|
|
150
|
+
**Subtypes MUST be substitutable for their base types.**
|
|
151
|
+
|
|
152
|
+
### MUST: Maintain Interface Contracts
|
|
153
|
+
|
|
154
|
+
**Derived components/hooks MUST maintain the same interface:**
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
// ✅ CORRECT - Maintains interface
|
|
158
|
+
interface BaseHook {
|
|
159
|
+
data: Data | null;
|
|
160
|
+
isLoading: boolean;
|
|
161
|
+
error: Error | null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function useBaseData(): BaseHook { ... }
|
|
165
|
+
|
|
166
|
+
function useExtendedData(): BaseHook {
|
|
167
|
+
const base = useBaseData();
|
|
168
|
+
// Extends but maintains same interface
|
|
169
|
+
return {
|
|
170
|
+
...base,
|
|
171
|
+
// Additional properties are optional
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Interface Segregation Principle (ISP)
|
|
177
|
+
|
|
178
|
+
**Clients SHOULD NOT depend on interfaces they don't use.**
|
|
179
|
+
|
|
180
|
+
### MUST: Create Focused Interfaces
|
|
181
|
+
|
|
182
|
+
**Interfaces SHOULD be small and focused:**
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
// ❌ WRONG - Large interface
|
|
186
|
+
interface UserService {
|
|
187
|
+
getUser(id: string): Promise<User>;
|
|
188
|
+
createUser(data: UserData): Promise<User>;
|
|
189
|
+
updateUser(id: string, data: Partial<User>): Promise<User>;
|
|
190
|
+
deleteUser(id: string): Promise<void>;
|
|
191
|
+
getUserEvents(id: string): Promise<Event[]>;
|
|
192
|
+
getUserOrganisations(id: string): Promise<Organisation[]>;
|
|
193
|
+
// ... many more methods
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ✅ CORRECT - Segregated interfaces
|
|
197
|
+
interface UserReader {
|
|
198
|
+
getUser(id: string): Promise<User>;
|
|
199
|
+
getUserEvents(id: string): Promise<Event[]>;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
interface UserWriter {
|
|
203
|
+
createUser(data: UserData): Promise<User>;
|
|
204
|
+
updateUser(id: string, data: Partial<User>): Promise<User>;
|
|
205
|
+
deleteUser(id: string): Promise<void>;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
interface UserOrganisationService {
|
|
209
|
+
getUserOrganisations(id: string): Promise<Organisation[]>;
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### SHOULD: Use Specific Props
|
|
214
|
+
|
|
215
|
+
**Component props SHOULD be specific, not generic:**
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
// ❌ WRONG - Generic props object
|
|
219
|
+
function UserCard({ user, config }: { user: User; config: any }) {
|
|
220
|
+
// ...
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ✅ CORRECT - Specific props
|
|
224
|
+
interface UserCardProps {
|
|
225
|
+
user: User;
|
|
226
|
+
showEmail?: boolean;
|
|
227
|
+
showAvatar?: boolean;
|
|
228
|
+
onEdit?: (user: User) => void;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function UserCard({ user, showEmail, showAvatar, onEdit }: UserCardProps) {
|
|
232
|
+
// ...
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Dependency Inversion Principle (DIP)
|
|
237
|
+
|
|
238
|
+
**High-level modules SHOULD NOT depend on low-level modules. Both SHOULD depend on abstractions.**
|
|
239
|
+
|
|
240
|
+
### MUST: Depend on Abstractions
|
|
241
|
+
|
|
242
|
+
**Depend on interfaces/types, not concrete implementations:**
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
// ❌ WRONG - Direct dependency on implementation
|
|
246
|
+
function UserService() {
|
|
247
|
+
const supabase = useSecureSupabase();
|
|
248
|
+
|
|
249
|
+
async function getUser(id: string) {
|
|
250
|
+
const { data } = await supabase
|
|
251
|
+
.from('users')
|
|
252
|
+
.select('*')
|
|
253
|
+
.eq('id', id)
|
|
254
|
+
.single();
|
|
255
|
+
return data;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return { getUser };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ✅ CORRECT - Abstracted interface
|
|
262
|
+
interface UserRepository {
|
|
263
|
+
getUser(id: string): Promise<User | null>;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function createUserRepository(supabase: SupabaseClient): UserRepository {
|
|
267
|
+
return {
|
|
268
|
+
async getUser(id: string) {
|
|
269
|
+
const { data } = await supabase
|
|
270
|
+
.from('users')
|
|
271
|
+
.select('*')
|
|
272
|
+
.eq('id', id)
|
|
273
|
+
.single();
|
|
274
|
+
return data;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function useUserService() {
|
|
280
|
+
const supabase = useSecureSupabase();
|
|
281
|
+
const repository = useMemo(
|
|
282
|
+
() => createUserRepository(supabase),
|
|
283
|
+
[supabase]
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
getUser: repository.getUser,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### SHOULD: Use Dependency Injection
|
|
293
|
+
|
|
294
|
+
**Inject dependencies rather than creating them:**
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
// ❌ WRONG - Hard-coded dependency
|
|
298
|
+
function EventService() {
|
|
299
|
+
const api = new ApiClient('https://api.example.com');
|
|
300
|
+
// ...
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ✅ CORRECT - Injected dependency
|
|
304
|
+
function EventService(api: ApiClient) {
|
|
305
|
+
// ...
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Or with React context
|
|
309
|
+
function useEventService() {
|
|
310
|
+
const api = useApiClient(); // From context
|
|
311
|
+
return useMemo(() => new EventService(api), [api]);
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## SOLID Checklist
|
|
316
|
+
|
|
317
|
+
Before committing code, verify:
|
|
318
|
+
- [ ] Each function/component has single responsibility
|
|
319
|
+
- [ ] Complex logic extracted to hooks/services
|
|
320
|
+
- [ ] Extension through composition, not modification
|
|
321
|
+
- [ ] Interfaces are focused and specific
|
|
322
|
+
- [ ] Dependencies are abstracted
|
|
323
|
+
- [ ] No god objects or bloated components
|
|
324
|
+
- [ ] Code is testable and maintainable
|
|
325
|
+
|
|
326
|
+
## Anti-Patterns to Avoid
|
|
327
|
+
|
|
328
|
+
1. **God Objects** - Classes/components that do too much
|
|
329
|
+
2. **Feature Envy** - Functions that use more of another object than their own
|
|
330
|
+
3. **Data Clumps** - Groups of data that should be objects
|
|
331
|
+
4. **Long Parameter Lists** - Use objects/interfaces instead
|
|
332
|
+
5. **Divergent Change** - One class changed for multiple reasons
|
|
333
|
+
6. **Shotgun Surgery** - One change requires many class modifications
|
|
334
|
+
|
|
335
|
+
## Reference
|
|
336
|
+
|
|
337
|
+
- Single Responsibility: Each module has one reason to change
|
|
338
|
+
- Open/Closed: Open for extension, closed for modification
|
|
339
|
+
- Liskov Substitution: Subtypes must be substitutable
|
|
340
|
+
- Interface Segregation: Many specific interfaces, not one general
|
|
341
|
+
- Dependency Inversion: Depend on abstractions, not concretions
|