@jmruthers/pace-core 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -10
- package/cursor-rules/00-pace-core-compliance.mdc +46 -87
- package/cursor-rules/01-standards-compliance.mdc +16 -47
- package/cursor-rules/02-project-structure.mdc +4 -4
- package/cursor-rules/03-solid-principles.mdc +45 -164
- package/cursor-rules/04-testing-standards.mdc +22 -69
- package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
- package/cursor-rules/06-code-quality.mdc +42 -125
- package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +18 -0
- package/cursor-rules/README.md +2 -1
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-Cb34EQs3.d.ts} +63 -1
- package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
- package/dist/{DataTable-DQ7RSOHE.js → DataTable-THFPBKTP.js} +12 -10
- package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DEMpysFR.d.ts} +394 -171
- package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +30 -8
- package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
- package/dist/{api-N774RPUA.js → api-IAGWF3ZG.js} +10 -10
- package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
- package/dist/{chunk-JBKQ3SAO.js → chunk-2T2IG7T7.js} +107 -57
- package/dist/chunk-2T2IG7T7.js.map +1 -0
- package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-3XTALGJF.js → chunk-6Z7LTB3D.js} +69 -240
- package/dist/chunk-6Z7LTB3D.js.map +1 -0
- package/dist/{chunk-4ZC4GX36.js → chunk-CNCQDFLN.js} +199 -46
- package/dist/chunk-CNCQDFLN.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-BYFSK72L.js → chunk-DWUBLJJM.js} +361 -187
- package/dist/chunk-DWUBLJJM.js.map +1 -0
- package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
- package/dist/chunk-FFQEQTNW.js.map +1 -0
- package/dist/chunk-FMUCXFII.js +76 -0
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-4N5C5XZU.js → chunk-HFZBI76P.js} +4 -4
- package/dist/chunk-HFZBI76P.js.map +1 -0
- package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-I7PSE6JW.js → chunk-M7MPQISP.js} +3 -76
- package/dist/chunk-M7MPQISP.js.map +1 -0
- package/dist/chunk-PQBSKX33.js +7793 -0
- package/dist/chunk-PQBSKX33.js.map +1 -0
- package/dist/chunk-QRPVRXYT.js +226 -0
- package/dist/chunk-QRPVRXYT.js.map +1 -0
- package/dist/{chunk-KNC55RTG.js → chunk-RWEBCB47.js} +194 -416
- package/dist/chunk-RWEBCB47.js.map +1 -0
- package/dist/{chunk-XM25TVIE.js → chunk-YDQHOZNA.js} +843 -388
- package/dist/chunk-YDQHOZNA.js.map +1 -0
- package/dist/{chunk-GLK6VM3F.js → chunk-ZNIWI3UC.js} +739 -737
- package/dist/chunk-ZNIWI3UC.js.map +1 -0
- package/dist/components.d.ts +5 -5
- package/dist/components.js +18 -16
- package/dist/components.js.map +1 -1
- package/dist/contextValidator-3JNZKUTX.js +9 -0
- package/dist/contextValidator-3JNZKUTX.js.map +1 -0
- package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
- package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
- package/dist/hooks.d.ts +55 -122
- package/dist/hooks.js +10 -13
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +60 -13
- package/dist/index.js +30 -25
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +21 -3
- package/dist/providers.js +4 -3
- package/dist/rbac/index.d.ts +210 -139
- package/dist/rbac/index.js +17 -13
- package/dist/styles/index.js +1 -1
- package/dist/theming/runtime.d.ts +1 -13
- package/dist/theming/runtime.js +2 -2
- package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
- package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
- package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +38 -17
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +17 -19
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +21 -17
- package/docs/api/modules.md +4191 -2967
- package/docs/architecture/database-schema-requirements.md +161 -0
- package/docs/components/context-selector.md +126 -0
- package/docs/core-concepts/rbac-system.md +3 -3
- package/docs/documentation-index.md +2 -4
- package/docs/getting-started/cursor-rules.md +2 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +2 -24
- package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/database-changes-december-2025.md +3 -3
- package/docs/pace-mint-fix-auto-selection.md +218 -0
- package/docs/pace-mint-rbac-setup.md +391 -0
- package/docs/rbac/event-based-apps.md +1 -1
- package/docs/rbac/getting-started.md +1 -1
- package/docs/rbac/quick-start.md +1 -1
- package/docs/rbac/secure-client-protection.md +330 -0
- package/docs/standards/README.md +1 -0
- package/package.json +4 -3
- package/scripts/audit/core/checks/accessibility.cjs +197 -0
- package/scripts/audit/core/checks/api-usage.cjs +191 -0
- package/scripts/audit/core/checks/bundle.cjs +142 -0
- package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +784 -685
- package/scripts/audit/core/checks/config.cjs +54 -0
- package/scripts/audit/core/checks/coverage.cjs +84 -0
- package/scripts/audit/core/checks/dependencies.cjs +985 -0
- package/scripts/audit/core/checks/documentation.cjs +268 -0
- package/scripts/audit/core/checks/environment.cjs +116 -0
- package/scripts/audit/core/checks/error-handling.cjs +340 -0
- package/scripts/audit/core/checks/forms.cjs +172 -0
- package/scripts/audit/core/checks/heuristics.cjs +68 -0
- package/scripts/audit/core/checks/hooks.cjs +334 -0
- package/scripts/audit/core/checks/imports.cjs +244 -0
- package/scripts/audit/core/checks/performance.cjs +325 -0
- package/scripts/audit/core/checks/routes.cjs +117 -0
- package/scripts/audit/core/checks/state.cjs +130 -0
- package/scripts/audit/core/checks/structure.cjs +65 -0
- package/scripts/audit/core/checks/style.cjs +584 -0
- package/scripts/audit/core/checks/testing.cjs +122 -0
- package/scripts/audit/core/checks/typescript.cjs +61 -0
- package/scripts/audit/core/scanner.cjs +199 -0
- package/scripts/audit/core/utils.cjs +137 -0
- package/scripts/audit/index.cjs +223 -0
- package/scripts/audit/reporters/console.cjs +151 -0
- package/scripts/audit/reporters/json.cjs +54 -0
- package/scripts/audit/reporters/markdown.cjs +124 -0
- package/scripts/audit-consuming-app.cjs +61 -936
- package/scripts/build-docs/build-decision.js +240 -0
- package/scripts/build-docs/cache-utils.js +105 -0
- package/scripts/build-docs/content-normalization.js +150 -0
- package/scripts/build-docs/file-utils.js +105 -0
- package/scripts/build-docs/git-utils.js +86 -0
- package/scripts/build-docs/hash-utils.js +116 -0
- package/scripts/build-docs/typedoc-runner.js +220 -0
- package/scripts/build-docs-incremental.js +77 -913
- package/scripts/utils/command-runner.js +16 -11
- package/scripts/validate-formats.js +61 -56
- package/scripts/validate-master.js +74 -69
- package/scripts/validate-pre-publish.js +70 -65
- package/src/__tests__/hooks/usePermissions.test.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +12 -18
- package/src/components/Alert/Alert.tsx +5 -7
- package/src/components/Avatar/Avatar.test.tsx +4 -4
- package/src/components/Badge/Badge.tsx +14 -0
- package/src/components/Button/Button.tsx +22 -0
- package/src/components/Calendar/Calendar.tsx +8 -2
- package/src/components/Card/Card.tsx +4 -0
- package/src/components/Checkbox/Checkbox.test.tsx +12 -12
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/ContextSelector/ContextSelector.tsx +384 -0
- package/src/components/ContextSelector/index.ts +3 -0
- package/src/components/DataTable/DataTable.tsx +38 -4
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
- package/src/components/DataTable/components/DataTableBody.tsx +8 -0
- package/src/components/DataTable/components/DataTableCore.tsx +196 -554
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
- package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
- package/src/components/DataTable/components/DataTableModals.tsx +8 -0
- package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
- package/src/components/DataTable/components/EditFields.tsx +307 -0
- package/src/components/DataTable/components/EditableRow.tsx +8 -0
- package/src/components/DataTable/components/EmptyState.tsx +10 -0
- package/src/components/DataTable/components/FilterRow.tsx +12 -0
- package/src/components/DataTable/components/GroupHeader.tsx +12 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
- package/src/components/DataTable/components/ImportModal.tsx +7 -0
- package/src/components/DataTable/components/LoadingState.tsx +6 -0
- package/src/components/DataTable/components/PaginationControls.tsx +16 -1
- package/src/components/DataTable/components/RowComponent.tsx +391 -0
- package/src/components/DataTable/components/UnifiedTableBody.tsx +63 -851
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
- package/src/components/DataTable/components/cellValueUtils.ts +40 -0
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
- package/src/components/DataTable/context/DataTableContext.tsx +50 -0
- package/src/components/DataTable/core/ColumnFactory.ts +31 -0
- package/src/components/DataTable/core/DataTableContext.tsx +32 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +127 -33
- package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
- package/src/components/DataTable/styles.ts +6 -6
- package/src/components/DataTable/types.ts +6 -10
- package/src/components/DataTable/utils/a11yUtils.ts +7 -0
- package/src/components/DataTable/utils/debugTools.ts +18 -113
- package/src/components/DataTable/utils/errorHandling.ts +12 -0
- package/src/components/DataTable/utils/exportUtils.ts +9 -0
- package/src/components/DataTable/utils/flexibleImport.ts +12 -48
- package/src/components/DataTable/utils/paginationUtils.ts +8 -0
- package/src/components/DataTable/utils/performanceUtils.ts +5 -1
- package/src/components/Dialog/Dialog.tsx +31 -3
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/FileDisplay/FileDisplay.tsx +74 -28
- package/src/components/FileUpload/FileUpload.tsx +22 -2
- package/src/components/Footer/Footer.test.tsx +16 -16
- package/src/components/Footer/Footer.tsx +14 -11
- package/src/components/Form/Form.tsx +1 -0
- package/src/components/Header/Header.test.tsx +43 -73
- package/src/components/Header/Header.tsx +59 -49
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +8 -4
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoginForm/LoginForm.tsx +4 -0
- package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
- package/src/components/NavigationMenu/types.ts +56 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +13 -11
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +167 -44
- package/src/components/PaceAppLayout/README.md +14 -17
- package/src/components/PaceAppLayout/test-setup.tsx +3 -4
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
- package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +80 -434
- package/src/components/Select/context.ts +23 -0
- package/src/components/Select/hooks/useSelectEvents.ts +87 -0
- package/src/components/Select/hooks/useSelectSearch.ts +91 -0
- package/src/components/Select/hooks/useSelectState.ts +104 -0
- package/src/components/Select/index.ts +9 -1
- package/src/components/Select/types.ts +123 -0
- package/src/components/Select/utils/text.ts +26 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Toast/Toast.tsx +4 -0
- package/src/components/Tooltip/Tooltip.tsx +2 -2
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +21 -18
- package/src/components/index.ts +7 -7
- package/src/eslint-rules/pace-core-compliance.cjs +106 -0
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +4 -0
- package/src/hooks/public/usePublicEventLogo.ts +4 -0
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +4 -0
- package/src/hooks/services/useAuth.ts +32 -0
- package/src/hooks/services/useCurrentEvent.ts +6 -0
- package/src/hooks/services/useCurrentOrganisation.ts +6 -0
- package/src/hooks/useAppConfig.ts +15 -30
- package/src/hooks/useDebounce.ts +9 -0
- package/src/hooks/useEventTheme.ts +6 -0
- package/src/hooks/useFileDisplay.ts +81 -50
- package/src/hooks/useFileReference.ts +25 -7
- package/src/hooks/useFileUrl.ts +11 -1
- package/src/hooks/useFocusManagement.ts +14 -0
- package/src/hooks/useFocusTrap.ts +3 -0
- package/src/hooks/useInactivityTracker.ts +3 -0
- package/src/hooks/useKeyboardShortcuts.ts +4 -0
- package/src/hooks/useOrganisationPermissions.ts +4 -0
- package/src/hooks/useOrganisationSecurity.ts +4 -0
- package/src/hooks/usePerformanceMonitor.ts +4 -0
- package/src/hooks/usePermissionCache.ts +7 -0
- package/src/hooks/useQueryCache.ts +12 -1
- package/src/hooks/useSessionRestoration.ts +4 -0
- package/src/hooks/useStorage.ts +4 -0
- package/src/hooks/useToast.ts +1 -1
- package/src/index.ts +6 -6
- package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
- package/src/providers/services/AuthServiceProvider.tsx +35 -7
- package/src/providers/services/EventServiceProvider.tsx +51 -5
- package/src/providers/services/InactivityServiceProvider.tsx +18 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +126 -134
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
- package/src/rbac/adapters.tsx +12 -3
- package/src/rbac/api.test.ts +59 -51
- package/src/rbac/api.ts +246 -167
- package/src/rbac/components/NavigationProvider.tsx +4 -1
- package/src/rbac/components/PagePermissionGuard.tsx +185 -17
- package/src/rbac/components/RoleBasedRouter.tsx +5 -1
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +20 -5
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
- package/src/rbac/engine.ts +38 -14
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
- package/src/rbac/hooks/permissions/index.ts +7 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
- package/src/rbac/hooks/permissions/useCan.ts +377 -0
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
- package/src/rbac/hooks/useCan.test.ts +64 -66
- package/src/rbac/hooks/usePermissions.ts +14 -995
- package/src/rbac/hooks/useRBAC.test.ts +1 -5
- package/src/rbac/hooks/useRBAC.ts +36 -37
- package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
- package/src/rbac/hooks/useResolvedScope.ts +35 -40
- package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
- package/src/rbac/hooks/useResourcePermissions.ts +14 -4
- package/src/rbac/hooks/useSecureSupabase.ts +27 -7
- package/src/rbac/index.ts +7 -0
- package/src/rbac/permissions.ts +0 -30
- package/src/rbac/secureClient.test.ts +22 -18
- package/src/rbac/secureClient.ts +294 -68
- package/src/rbac/security.ts +0 -17
- package/src/rbac/types.ts +9 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
- package/src/rbac/utils/clientSecurity.ts +93 -0
- package/src/rbac/utils/contextValidator.ts +77 -168
- package/src/services/AuthService.ts +39 -7
- package/src/services/EventService.ts +186 -54
- package/src/services/OrganisationService.ts +81 -14
- package/src/services/__tests__/EventService.test.ts +1 -2
- package/src/services/base/BaseService.ts +3 -0
- package/src/theming/__tests__/parseEventColours.test.ts +6 -9
- package/src/theming/parseEventColours.ts +5 -19
- package/src/types/vitest-globals.d.ts +51 -26
- package/src/utils/__mocks__/supabaseMock.ts +1 -3
- package/src/utils/__tests__/formatting.unit.test.ts +4 -4
- package/src/utils/__tests__/index.unit.test.ts +2 -2
- package/src/utils/audit/audit.ts +0 -3
- package/src/utils/core/cn.ts +1 -1
- package/src/utils/dynamic/dynamicUtils.ts +7 -4
- package/src/utils/file-reference/index.ts +53 -1
- package/src/utils/formatting/formatting.ts +8 -18
- package/src/utils/index.ts +0 -1
- package/dist/chunk-3QRJFVBR.js.map +0 -1
- package/dist/chunk-3XTALGJF.js.map +0 -1
- package/dist/chunk-4N5C5XZU.js.map +0 -1
- package/dist/chunk-4ZC4GX36.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js +0 -38
- package/dist/chunk-BYFSK72L.js.map +0 -1
- package/dist/chunk-EXUD6RNJ.js +0 -451
- package/dist/chunk-EXUD6RNJ.js.map +0 -1
- package/dist/chunk-GLK6VM3F.js.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-JBKQ3SAO.js.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-LXQLPRQ2.js.map +0 -1
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-T33XF5ZC.js +0 -12922
- package/dist/chunk-T33XF5ZC.js.map +0 -1
- package/dist/chunk-XM25TVIE.js.map +0 -1
- package/docs/api/classes/ColumnFactory.md +0 -243
- package/docs/api/classes/ErrorBoundary.md +0 -144
- package/docs/api/classes/InvalidScopeError.md +0 -73
- package/docs/api/classes/Logger.md +0 -178
- package/docs/api/classes/MissingUserContextError.md +0 -66
- package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
- package/docs/api/classes/PermissionDeniedError.md +0 -73
- package/docs/api/classes/RBACAuditManager.md +0 -297
- package/docs/api/classes/RBACCache.md +0 -322
- package/docs/api/classes/RBACEngine.md +0 -171
- package/docs/api/classes/RBACError.md +0 -76
- package/docs/api/classes/RBACNotInitializedError.md +0 -66
- package/docs/api/classes/SecureSupabaseClient.md +0 -160
- package/docs/api/classes/StorageUtils.md +0 -328
- package/docs/api/enums/FileCategory.md +0 -184
- package/docs/api/enums/LogLevel.md +0 -54
- package/docs/api/enums/RBACErrorCode.md +0 -228
- package/docs/api/enums/RPCFunction.md +0 -118
- package/docs/api/interfaces/AddressFieldProps.md +0 -241
- package/docs/api/interfaces/AddressFieldRef.md +0 -94
- package/docs/api/interfaces/AggregateConfig.md +0 -43
- package/docs/api/interfaces/AutocompleteOptions.md +0 -75
- package/docs/api/interfaces/AvatarProps.md +0 -128
- package/docs/api/interfaces/BadgeProps.md +0 -27
- package/docs/api/interfaces/ButtonProps.md +0 -53
- package/docs/api/interfaces/CalendarProps.md +0 -70
- package/docs/api/interfaces/CardProps.md +0 -66
- package/docs/api/interfaces/ColorPalette.md +0 -7
- package/docs/api/interfaces/ColorShade.md +0 -66
- package/docs/api/interfaces/ComplianceResult.md +0 -30
- package/docs/api/interfaces/DataAccessRecord.md +0 -96
- package/docs/api/interfaces/DataRecord.md +0 -11
- package/docs/api/interfaces/DataTableAction.md +0 -249
- package/docs/api/interfaces/DataTableColumn.md +0 -504
- package/docs/api/interfaces/DataTableProps.md +0 -625
- package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
- package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
- package/docs/api/interfaces/DatabaseIssue.md +0 -41
- package/docs/api/interfaces/EmptyStateConfig.md +0 -61
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
- package/docs/api/interfaces/EventAppRoleData.md +0 -71
- package/docs/api/interfaces/ExportColumn.md +0 -90
- package/docs/api/interfaces/ExportOptions.md +0 -126
- package/docs/api/interfaces/FileDisplayProps.md +0 -249
- package/docs/api/interfaces/FileMetadata.md +0 -129
- package/docs/api/interfaces/FileReference.md +0 -118
- package/docs/api/interfaces/FileSizeLimits.md +0 -7
- package/docs/api/interfaces/FileUploadOptions.md +0 -139
- package/docs/api/interfaces/FileUploadProps.md +0 -293
- package/docs/api/interfaces/FooterProps.md +0 -105
- package/docs/api/interfaces/FormFieldProps.md +0 -166
- package/docs/api/interfaces/FormProps.md +0 -113
- package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
- package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
- package/docs/api/interfaces/InputProps.md +0 -53
- package/docs/api/interfaces/LabelProps.md +0 -107
- package/docs/api/interfaces/LoggerConfig.md +0 -62
- package/docs/api/interfaces/LoginFormProps.md +0 -184
- package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
- package/docs/api/interfaces/NavigationContextType.md +0 -164
- package/docs/api/interfaces/NavigationGuardProps.md +0 -139
- package/docs/api/interfaces/NavigationItem.md +0 -120
- package/docs/api/interfaces/NavigationMenuProps.md +0 -221
- package/docs/api/interfaces/NavigationProviderProps.md +0 -117
- package/docs/api/interfaces/Organisation.md +0 -140
- package/docs/api/interfaces/OrganisationContextType.md +0 -388
- package/docs/api/interfaces/OrganisationMembership.md +0 -140
- package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
- package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
- package/docs/api/interfaces/PaceAppLayoutProps.md +0 -406
- package/docs/api/interfaces/PaceLoginPageProps.md +0 -47
- package/docs/api/interfaces/PageAccessRecord.md +0 -85
- package/docs/api/interfaces/PagePermissionContextType.md +0 -140
- package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
- package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
- package/docs/api/interfaces/PaletteData.md +0 -41
- package/docs/api/interfaces/ParsedAddress.md +0 -120
- package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
- package/docs/api/interfaces/ProgressProps.md +0 -42
- package/docs/api/interfaces/ProtectedRouteProps.md +0 -97
- package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
- package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
- package/docs/api/interfaces/PublicPageLayoutProps.md +0 -198
- package/docs/api/interfaces/QuickFix.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
- package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
- package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
- package/docs/api/interfaces/RBACConfig.md +0 -133
- package/docs/api/interfaces/RBACContext.md +0 -52
- package/docs/api/interfaces/RBACLogger.md +0 -112
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
- package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
- package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
- package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
- package/docs/api/interfaces/RBACResult.md +0 -58
- package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
- package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
- package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
- package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
- package/docs/api/interfaces/RBACRolesListParams.md +0 -52
- package/docs/api/interfaces/RBACRolesListResult.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
- package/docs/api/interfaces/ResourcePermissions.md +0 -155
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
- package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
- package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
- package/docs/api/interfaces/RoleManagementResult.md +0 -52
- package/docs/api/interfaces/RouteAccessRecord.md +0 -107
- package/docs/api/interfaces/RouteConfig.md +0 -134
- package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
- package/docs/api/interfaces/SecureDataContextType.md +0 -168
- package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
- package/docs/api/interfaces/SetupIssue.md +0 -41
- package/docs/api/interfaces/StorageConfig.md +0 -41
- package/docs/api/interfaces/StorageFileInfo.md +0 -74
- package/docs/api/interfaces/StorageFileMetadata.md +0 -151
- package/docs/api/interfaces/StorageListOptions.md +0 -99
- package/docs/api/interfaces/StorageListResult.md +0 -41
- package/docs/api/interfaces/StorageUploadOptions.md +0 -101
- package/docs/api/interfaces/StorageUploadResult.md +0 -63
- package/docs/api/interfaces/StorageUrlOptions.md +0 -60
- package/docs/api/interfaces/StyleImport.md +0 -19
- package/docs/api/interfaces/SwitchProps.md +0 -34
- package/docs/api/interfaces/TabsContentProps.md +0 -9
- package/docs/api/interfaces/TabsListProps.md +0 -9
- package/docs/api/interfaces/TabsProps.md +0 -9
- package/docs/api/interfaces/TabsTriggerProps.md +0 -50
- package/docs/api/interfaces/TextareaProps.md +0 -53
- package/docs/api/interfaces/ToastActionElement.md +0 -9
- package/docs/api/interfaces/ToastProps.md +0 -9
- package/docs/api/interfaces/UnifiedAuthContextType.md +0 -820
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -171
- package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
- package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -136
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
- package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
- package/docs/api/interfaces/UsePublicEventReturn.md +0 -68
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -120
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -94
- package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
- package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
- package/docs/api/interfaces/UserEventAccess.md +0 -118
- package/docs/api/interfaces/UserMenuProps.md +0 -86
- package/docs/api/interfaces/UserProfile.md +0 -63
- package/docs/migration/quick-migration-guide.md +0 -356
- package/docs/migration/service-architecture.md +0 -281
- package/src/components/EventSelector/EventSelector.test.tsx +0 -720
- package/src/components/EventSelector/EventSelector.tsx +0 -420
- package/src/components/EventSelector/index.ts +0 -3
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -324
- package/src/components/OrganisationSelector/index.ts +0 -9
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
- package/src/hooks/useSecureDataAccess.test.ts +0 -559
- package/src/hooks/useSecureDataAccess.ts +0 -681
- /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-THFPBKTP.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
- /package/dist/{api-N774RPUA.js.map → api-IAGWF3ZG.js.map} +0 -0
- /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
- /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
- /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
- /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
- /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Enforce SOLID architecture principles in consuming apps
|
|
3
3
|
globs: ["src/**/*.{ts,tsx}"]
|
|
4
|
-
alwaysApply:
|
|
5
|
-
paceCoreVersion: "0.
|
|
6
|
-
rulesVersion: "2025-01-
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
paceCoreVersion: "0.6.x"
|
|
6
|
+
rulesVersion: "2025-01-28"
|
|
7
7
|
---
|
|
8
8
|
# SOLID Principles Guide
|
|
9
9
|
|
|
@@ -18,36 +18,19 @@ This guide enforces SOLID architecture principles to ensure maintainable, extens
|
|
|
18
18
|
**Each function/component SHOULD do one thing:**
|
|
19
19
|
|
|
20
20
|
```tsx
|
|
21
|
-
// ❌ WRONG
|
|
22
|
-
function UserProfile({ userId }
|
|
21
|
+
// ❌ WRONG: Multiple responsibilities (fetching, formatting, rendering in one component)
|
|
22
|
+
function UserProfile({ userId }) {
|
|
23
23
|
const [user, setUser] = useState(null);
|
|
24
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
|
-
|
|
25
|
+
useEffect(() => { fetchUser(userId).then(setUser); fetchEvents(userId).then(setEvents); }, [userId]);
|
|
37
26
|
return <div>...</div>;
|
|
38
27
|
}
|
|
39
28
|
|
|
40
|
-
// ✅ CORRECT
|
|
41
|
-
function UserProfile({ userId }
|
|
29
|
+
// ✅ CORRECT: Separated responsibilities (hooks for data, components for UI)
|
|
30
|
+
function UserProfile({ userId }) {
|
|
42
31
|
const user = useUser(userId);
|
|
43
32
|
const events = useUserEvents(userId);
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<div>
|
|
47
|
-
<UserInfo user={user} />
|
|
48
|
-
<UserEvents events={events} />
|
|
49
|
-
</div>
|
|
50
|
-
);
|
|
33
|
+
return <div><UserInfo user={user} /><UserEvents events={events} /></div>;
|
|
51
34
|
}
|
|
52
35
|
```
|
|
53
36
|
|
|
@@ -56,37 +39,19 @@ function UserProfile({ userId }: { userId: string }) {
|
|
|
56
39
|
**Complex logic SHOULD be extracted into hooks, services, or utilities:**
|
|
57
40
|
|
|
58
41
|
```tsx
|
|
59
|
-
// ❌ WRONG
|
|
42
|
+
// ❌ WRONG: Business logic in component (filtering, sorting, formatting)
|
|
60
43
|
function EventList() {
|
|
61
44
|
const [events, setEvents] = useState([]);
|
|
62
45
|
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
|
-
|
|
46
|
+
useEffect(() => { /* complex filtering/sorting logic */ }, [events]);
|
|
76
47
|
return <div>{filtered.map(...)}</div>;
|
|
77
48
|
}
|
|
78
49
|
|
|
79
|
-
// ✅ CORRECT
|
|
50
|
+
// ✅ CORRECT: Logic extracted to hook
|
|
80
51
|
function useFilteredEvents() {
|
|
81
52
|
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]);
|
|
53
|
+
return useMemo(() => events.filter(...).sort(...).map(...), [events]);
|
|
88
54
|
}
|
|
89
|
-
|
|
90
55
|
function EventList() {
|
|
91
56
|
const filteredEvents = useFilteredEvents();
|
|
92
57
|
return <div>{filteredEvents.map(...)}</div>;
|
|
@@ -102,28 +67,16 @@ function EventList() {
|
|
|
102
67
|
**Extend functionality through composition, not modification:**
|
|
103
68
|
|
|
104
69
|
```tsx
|
|
105
|
-
// ❌ WRONG
|
|
106
|
-
function BaseButton({ onClick, ...props }) {
|
|
107
|
-
return <button onClick={onClick} {...props} />;
|
|
108
|
-
}
|
|
109
|
-
|
|
70
|
+
// ❌ WRONG: Modifying base component behavior
|
|
110
71
|
function SpecialButton({ onClick, ...props }) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
trackEvent('button-click');
|
|
114
|
-
onClick?.(e);
|
|
115
|
-
};
|
|
116
|
-
return <BaseButton onClick={handleClick} {...props} />;
|
|
72
|
+
const handleClick = (e) => { trackEvent('button-click'); onClick?.(e); };
|
|
73
|
+
return <BaseButton onClick={handleClick} {...props} />; // Modifies base
|
|
117
74
|
}
|
|
118
75
|
|
|
119
|
-
// ✅ CORRECT
|
|
76
|
+
// ✅ CORRECT: Composition with pace-core (extend, don't modify)
|
|
120
77
|
import { Button } from '@jmruthers/pace-core';
|
|
121
|
-
|
|
122
78
|
function SpecialButton({ onClick, ...props }) {
|
|
123
|
-
const handleClick = (e) => {
|
|
124
|
-
trackEvent('button-click');
|
|
125
|
-
onClick?.(e);
|
|
126
|
-
};
|
|
79
|
+
const handleClick = (e) => { trackEvent('button-click'); onClick?.(e); };
|
|
127
80
|
return <Button onClick={handleClick} {...props} />;
|
|
128
81
|
}
|
|
129
82
|
```
|
|
@@ -133,13 +86,8 @@ function SpecialButton({ onClick, ...props }) {
|
|
|
133
86
|
**Extend behavior through configuration:**
|
|
134
87
|
|
|
135
88
|
```tsx
|
|
136
|
-
// ✅ CORRECT
|
|
137
|
-
interface DataTableConfig {
|
|
138
|
-
columns: Column[];
|
|
139
|
-
features: FeatureConfig;
|
|
140
|
-
rbac: RBACConfig;
|
|
141
|
-
}
|
|
142
|
-
|
|
89
|
+
// ✅ CORRECT: Extend through configuration, not code changes
|
|
90
|
+
interface DataTableConfig { columns: Column[]; features: FeatureConfig; rbac: RBACConfig; }
|
|
143
91
|
function MyDataTable({ config }: { config: DataTableConfig }) {
|
|
144
92
|
return <DataTable {...config} />;
|
|
145
93
|
}
|
|
@@ -154,22 +102,12 @@ function MyDataTable({ config }: { config: DataTableConfig }) {
|
|
|
154
102
|
**Derived components/hooks MUST maintain the same interface:**
|
|
155
103
|
|
|
156
104
|
```tsx
|
|
157
|
-
// ✅ CORRECT
|
|
158
|
-
interface BaseHook {
|
|
159
|
-
data: Data | null;
|
|
160
|
-
isLoading: boolean;
|
|
161
|
-
error: Error | null;
|
|
162
|
-
}
|
|
163
|
-
|
|
105
|
+
// ✅ CORRECT: Derived hooks maintain same interface (substitutable)
|
|
106
|
+
interface BaseHook { data: Data | null; isLoading: boolean; error: Error | null; }
|
|
164
107
|
function useBaseData(): BaseHook { ... }
|
|
165
|
-
|
|
166
108
|
function useExtendedData(): BaseHook {
|
|
167
109
|
const base = useBaseData();
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
...base,
|
|
171
|
-
// Additional properties are optional
|
|
172
|
-
};
|
|
110
|
+
return { ...base }; // Maintains interface, extends behavior
|
|
173
111
|
}
|
|
174
112
|
```
|
|
175
113
|
|
|
@@ -182,7 +120,7 @@ function useExtendedData(): BaseHook {
|
|
|
182
120
|
**Interfaces SHOULD be small and focused:**
|
|
183
121
|
|
|
184
122
|
```tsx
|
|
185
|
-
// ❌ WRONG
|
|
123
|
+
// ❌ WRONG: Large interface with many responsibilities
|
|
186
124
|
interface UserService {
|
|
187
125
|
getUser(id: string): Promise<User>;
|
|
188
126
|
createUser(data: UserData): Promise<User>;
|
|
@@ -190,24 +128,12 @@ interface UserService {
|
|
|
190
128
|
deleteUser(id: string): Promise<void>;
|
|
191
129
|
getUserEvents(id: string): Promise<Event[]>;
|
|
192
130
|
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
131
|
}
|
|
201
132
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
interface UserOrganisationService {
|
|
209
|
-
getUserOrganisations(id: string): Promise<Organisation[]>;
|
|
210
|
-
}
|
|
133
|
+
// ✅ CORRECT: Segregated interfaces (focused, specific)
|
|
134
|
+
interface UserReader { getUser(id: string): Promise<User>; getUserEvents(id: string): Promise<Event[]>; }
|
|
135
|
+
interface UserWriter { createUser(data: UserData): Promise<User>; updateUser(id: string, data: Partial<User>): Promise<User>; deleteUser(id: string): Promise<void>; }
|
|
136
|
+
interface UserOrganisationService { getUserOrganisations(id: string): Promise<Organisation[]>; }
|
|
211
137
|
```
|
|
212
138
|
|
|
213
139
|
### SHOULD: Use Specific Props
|
|
@@ -215,22 +141,12 @@ interface UserOrganisationService {
|
|
|
215
141
|
**Component props SHOULD be specific, not generic:**
|
|
216
142
|
|
|
217
143
|
```tsx
|
|
218
|
-
// ❌ WRONG
|
|
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
|
-
}
|
|
144
|
+
// ❌ WRONG: Generic props object (config: any)
|
|
145
|
+
function UserCard({ user, config }: { user: User; config: any }) { ... }
|
|
230
146
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
147
|
+
// ✅ CORRECT: Specific, focused props interface
|
|
148
|
+
interface UserCardProps { user: User; showEmail?: boolean; showAvatar?: boolean; onEdit?: (user: User) => void; }
|
|
149
|
+
function UserCard({ user, showEmail, showAvatar, onEdit }: UserCardProps) { ... }
|
|
234
150
|
```
|
|
235
151
|
|
|
236
152
|
## Dependency Inversion Principle (DIP)
|
|
@@ -242,50 +158,25 @@ function UserCard({ user, showEmail, showAvatar, onEdit }: UserCardProps) {
|
|
|
242
158
|
**Depend on interfaces/types, not concrete implementations:**
|
|
243
159
|
|
|
244
160
|
```tsx
|
|
245
|
-
// ❌ WRONG
|
|
161
|
+
// ❌ WRONG: Direct dependency on implementation (tight coupling)
|
|
246
162
|
function UserService() {
|
|
247
163
|
const supabase = useSecureSupabase();
|
|
248
|
-
|
|
249
164
|
async function getUser(id: string) {
|
|
250
|
-
const { data } = await supabase
|
|
251
|
-
.from('users')
|
|
252
|
-
.select('*')
|
|
253
|
-
.eq('id', id)
|
|
254
|
-
.single();
|
|
165
|
+
const { data } = await supabase.from('users').select('*').eq('id', id).single();
|
|
255
166
|
return data;
|
|
256
167
|
}
|
|
257
|
-
|
|
258
168
|
return { getUser };
|
|
259
169
|
}
|
|
260
170
|
|
|
261
|
-
// ✅ CORRECT
|
|
262
|
-
interface UserRepository {
|
|
263
|
-
getUser(id: string): Promise<User | null>;
|
|
264
|
-
}
|
|
265
|
-
|
|
171
|
+
// ✅ CORRECT: Abstracted interface (depend on abstraction)
|
|
172
|
+
interface UserRepository { getUser(id: string): Promise<User | null>; }
|
|
266
173
|
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
|
-
};
|
|
174
|
+
return { async getUser(id: string) { /* implementation */ } };
|
|
277
175
|
}
|
|
278
|
-
|
|
279
176
|
function useUserService() {
|
|
280
177
|
const supabase = useSecureSupabase();
|
|
281
|
-
const repository = useMemo(
|
|
282
|
-
|
|
283
|
-
[supabase]
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
return {
|
|
287
|
-
getUser: repository.getUser,
|
|
288
|
-
};
|
|
178
|
+
const repository = useMemo(() => createUserRepository(supabase), [supabase]);
|
|
179
|
+
return { getUser: repository.getUser };
|
|
289
180
|
}
|
|
290
181
|
```
|
|
291
182
|
|
|
@@ -294,22 +185,12 @@ function useUserService() {
|
|
|
294
185
|
**Inject dependencies rather than creating them:**
|
|
295
186
|
|
|
296
187
|
```tsx
|
|
297
|
-
// ❌ WRONG
|
|
298
|
-
function EventService() {
|
|
299
|
-
const api = new ApiClient('https://api.example.com');
|
|
300
|
-
// ...
|
|
301
|
-
}
|
|
188
|
+
// ❌ WRONG: Hard-coded dependency
|
|
189
|
+
function EventService() { const api = new ApiClient('https://api.example.com'); }
|
|
302
190
|
|
|
303
|
-
// ✅ CORRECT
|
|
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
|
-
}
|
|
191
|
+
// ✅ CORRECT: Injected dependency (dependency injection)
|
|
192
|
+
function EventService(api: ApiClient) { ... }
|
|
193
|
+
// Or with React context: const api = useApiClient(); useMemo(() => new EventService(api), [api]);
|
|
313
194
|
```
|
|
314
195
|
|
|
315
196
|
## SOLID Checklist
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Enforce testing framework consistency and standards for consuming apps
|
|
3
3
|
globs: ["**/*.{test,spec}.{ts,tsx}"]
|
|
4
|
-
alwaysApply:
|
|
5
|
-
paceCoreVersion: "0.
|
|
6
|
-
rulesVersion: "2025-01-
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
paceCoreVersion: "0.6.x"
|
|
6
|
+
rulesVersion: "2025-01-28"
|
|
7
7
|
---
|
|
8
8
|
# Testing Standards Guide
|
|
9
9
|
|
|
@@ -25,24 +25,18 @@ npm run test:coverage
|
|
|
25
25
|
**MUST use React Testing Library + userEvent for all component tests.**
|
|
26
26
|
|
|
27
27
|
```tsx
|
|
28
|
-
// ✅ CORRECT
|
|
28
|
+
// ✅ CORRECT: React Testing Library + userEvent
|
|
29
29
|
import { render, screen } from '@testing-library/react';
|
|
30
30
|
import userEvent from '@testing-library/user-event';
|
|
31
|
-
import { Button } from '@jmruthers/pace-core';
|
|
32
|
-
|
|
33
31
|
test('button clicks work', async () => {
|
|
34
32
|
const user = userEvent.setup();
|
|
35
33
|
const handleClick = vi.fn();
|
|
36
|
-
|
|
37
34
|
render(<Button onClick={handleClick}>Click me</Button>);
|
|
38
|
-
|
|
39
35
|
await user.click(screen.getByRole('button', { name: /click me/i }));
|
|
40
|
-
|
|
41
36
|
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
42
37
|
});
|
|
43
38
|
|
|
44
|
-
// ❌ WRONG
|
|
45
|
-
import { shallow } from 'enzyme';
|
|
39
|
+
// ❌ WRONG: Enzyme or other testing libraries
|
|
46
40
|
```
|
|
47
41
|
|
|
48
42
|
## MUST: Colocate Tests
|
|
@@ -94,21 +88,15 @@ export default defineConfig({
|
|
|
94
88
|
**Tests MUST focus on what users see and do:**
|
|
95
89
|
|
|
96
90
|
```tsx
|
|
97
|
-
// ❌ WRONG
|
|
98
|
-
test('calls setState', () => {
|
|
99
|
-
const component = render(<Counter />);
|
|
100
|
-
expect(component.state.count).toBe(0);
|
|
101
|
-
});
|
|
91
|
+
// ❌ WRONG: Testing implementation (component.state.count)
|
|
92
|
+
test('calls setState', () => { const component = render(<Counter />); expect(component.state.count).toBe(0); });
|
|
102
93
|
|
|
103
|
-
// ✅ CORRECT
|
|
94
|
+
// ✅ CORRECT: Testing user behavior (what user sees and does)
|
|
104
95
|
test('displays count and increments on button click', async () => {
|
|
105
96
|
const user = userEvent.setup();
|
|
106
97
|
render(<Counter />);
|
|
107
|
-
|
|
108
98
|
expect(screen.getByText('Count: 0')).toBeInTheDocument();
|
|
109
|
-
|
|
110
99
|
await user.click(screen.getByRole('button', { name: /increment/i }));
|
|
111
|
-
|
|
112
100
|
expect(screen.getByText('Count: 1')).toBeInTheDocument();
|
|
113
101
|
});
|
|
114
102
|
```
|
|
@@ -118,14 +106,11 @@ test('displays count and increments on button click', async () => {
|
|
|
118
106
|
**MUST prefer accessible queries (byRole, byLabelText, etc.):**
|
|
119
107
|
|
|
120
108
|
```tsx
|
|
121
|
-
// ✅ CORRECT
|
|
109
|
+
// ✅ CORRECT: Accessible queries (byRole, byLabelText, byText)
|
|
122
110
|
screen.getByRole('button', { name: /submit/i });
|
|
123
111
|
screen.getByLabelText(/email address/i);
|
|
124
|
-
screen.getByText(/welcome/i);
|
|
125
112
|
|
|
126
|
-
// ❌ AVOID
|
|
127
|
-
screen.getByTestId('submit-button');
|
|
128
|
-
screen.getByClassName('btn-primary');
|
|
113
|
+
// ❌ AVOID: Non-accessible queries (getByTestId, getByClassName - use as last resort)
|
|
129
114
|
```
|
|
130
115
|
|
|
131
116
|
## SHOULD: Test Critical Paths
|
|
@@ -174,19 +159,10 @@ describe('EventCard', () => {
|
|
|
174
159
|
**MUST NOT mock unless necessary:**
|
|
175
160
|
|
|
176
161
|
```tsx
|
|
177
|
-
// ❌ WRONG
|
|
178
|
-
|
|
179
|
-
global.fetch = mockFetch;
|
|
180
|
-
|
|
181
|
-
// ✅ CORRECT - Use real implementation or MSW
|
|
162
|
+
// ❌ WRONG: Unnecessary mock (global.fetch = mockFetch)
|
|
163
|
+
// ✅ CORRECT: Use real implementation or MSW
|
|
182
164
|
import { server } from './mocks/server';
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
server.use(
|
|
186
|
-
rest.get('/api/events', (req, res, ctx) => {
|
|
187
|
-
return res(ctx.json([{ id: '1', name: 'Event' }]));
|
|
188
|
-
})
|
|
189
|
-
);
|
|
165
|
+
server.use(rest.get('/api/events', (req, res, ctx) => res(ctx.json([{ id: '1', name: 'Event' }]))));
|
|
190
166
|
```
|
|
191
167
|
|
|
192
168
|
## MUST: Test Async Code Properly
|
|
@@ -194,16 +170,11 @@ server.use(
|
|
|
194
170
|
**MUST handle async operations correctly:**
|
|
195
171
|
|
|
196
172
|
```tsx
|
|
197
|
-
// ✅ CORRECT
|
|
173
|
+
// ✅ CORRECT: Async testing with waitFor
|
|
198
174
|
test('loads and displays events', async () => {
|
|
199
175
|
render(<EventList />);
|
|
200
|
-
|
|
201
176
|
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
|
202
|
-
|
|
203
|
-
await waitFor(() => {
|
|
204
|
-
expect(screen.getByText('Event 1')).toBeInTheDocument();
|
|
205
|
-
});
|
|
206
|
-
|
|
177
|
+
await waitFor(() => expect(screen.getByText('Event 1')).toBeInTheDocument());
|
|
207
178
|
expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();
|
|
208
179
|
});
|
|
209
180
|
```
|
|
@@ -213,11 +184,8 @@ test('loads and displays events', async () => {
|
|
|
213
184
|
**MUST clean up resources:**
|
|
214
185
|
|
|
215
186
|
```tsx
|
|
216
|
-
// ✅ CORRECT
|
|
217
|
-
afterEach(() => {
|
|
218
|
-
cleanup();
|
|
219
|
-
vi.clearAllMocks();
|
|
220
|
-
});
|
|
187
|
+
// ✅ CORRECT: Cleanup after tests
|
|
188
|
+
afterEach(() => { cleanup(); vi.clearAllMocks(); });
|
|
221
189
|
```
|
|
222
190
|
|
|
223
191
|
## SHOULD: Use Test Utilities
|
|
@@ -225,23 +193,11 @@ afterEach(() => {
|
|
|
225
193
|
**SHOULD create reusable test utilities:**
|
|
226
194
|
|
|
227
195
|
```tsx
|
|
228
|
-
// test
|
|
229
|
-
import { render } from '@testing-library/react';
|
|
230
|
-
import { UnifiedAuthProvider } from '@jmruthers/pace-core';
|
|
231
|
-
|
|
196
|
+
// ✅ CORRECT: Reusable test utilities
|
|
232
197
|
export function renderWithProviders(ui: React.ReactElement) {
|
|
233
|
-
return render(
|
|
234
|
-
<UnifiedAuthProvider supabaseClient={mockSupabase} appName="Test App">
|
|
235
|
-
{ui}
|
|
236
|
-
</UnifiedAuthProvider>
|
|
237
|
-
);
|
|
198
|
+
return render(<UnifiedAuthProvider supabaseClient={mockSupabase} appName="Test App">{ui}</UnifiedAuthProvider>);
|
|
238
199
|
}
|
|
239
|
-
|
|
240
|
-
// Usage
|
|
241
|
-
test('component works with providers', () => {
|
|
242
|
-
renderWithProviders(<MyComponent />);
|
|
243
|
-
// ...
|
|
244
|
-
});
|
|
200
|
+
// Usage: renderWithProviders(<MyComponent />);
|
|
245
201
|
```
|
|
246
202
|
|
|
247
203
|
## MUST: Include Timeout Parameters
|
|
@@ -249,12 +205,9 @@ test('component works with providers', () => {
|
|
|
249
205
|
**Tests MUST include timeout parameters to prevent hanging:**
|
|
250
206
|
|
|
251
207
|
```tsx
|
|
252
|
-
// ✅ CORRECT
|
|
208
|
+
// ✅ CORRECT: Include timeout parameters to prevent hanging
|
|
253
209
|
test('async operation completes', async () => {
|
|
254
|
-
await waitFor(
|
|
255
|
-
() => expect(screen.getByText('Loaded')).toBeInTheDocument(),
|
|
256
|
-
{ timeout: 5000 }
|
|
257
|
-
);
|
|
210
|
+
await waitFor(() => expect(screen.getByText('Loaded')).toBeInTheDocument(), { timeout: 5000 });
|
|
258
211
|
}, { timeout: 10000 });
|
|
259
212
|
```
|
|
260
213
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
description: Standardized templates for bug reports and feature requests for pace-core
|
|
3
3
|
globs: []
|
|
4
4
|
alwaysApply: false
|
|
5
|
-
paceCoreVersion: "0.
|
|
6
|
-
rulesVersion: "2025-01-
|
|
5
|
+
paceCoreVersion: "0.6.x"
|
|
6
|
+
rulesVersion: "2025-01-28"
|
|
7
7
|
---
|
|
8
8
|
# Bug Reports and Feature Requests Guide
|
|
9
9
|
|