@jmruthers/pace-core 0.6.1 → 0.6.2
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 +43 -10
- package/cursor-rules/00-pace-core-compliance.mdc +18 -91
- 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-BPvc3Ka0.d.ts} +54 -0
- package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
- package/dist/{DataTable-DQ7RSOHE.js → DataTable-TPTKCX4D.js} +10 -9
- package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +356 -111
- package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
- package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
- package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
- package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
- package/dist/chunk-24UVZUZG.js.map +1 -0
- package/dist/{chunk-4N5C5XZU.js → chunk-2UOI2FG5.js} +4 -4
- package/dist/chunk-2UOI2FG5.js.map +1 -0
- package/dist/{chunk-T33XF5ZC.js → chunk-3XC4CPTD.js} +4317 -3963
- package/dist/chunk-3XC4CPTD.js.map +1 -0
- package/dist/{chunk-4ZC4GX36.js → chunk-6J4GEEJR.js} +172 -45
- package/dist/chunk-6J4GEEJR.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-BYFSK72L.js → chunk-EHMR7VYL.js} +4 -4
- package/dist/chunk-EHMR7VYL.js.map +1 -0
- package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
- package/dist/chunk-F2IMUDXZ.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-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-3XTALGJF.js → chunk-MMZ7JXPU.js} +60 -223
- package/dist/chunk-MMZ7JXPU.js.map +1 -0
- package/dist/{chunk-GLK6VM3F.js → chunk-NECFR5MM.js} +254 -170
- package/dist/chunk-NECFR5MM.js.map +1 -0
- package/dist/{chunk-JBKQ3SAO.js → chunk-SFZUDBL5.js} +40 -4
- package/dist/chunk-SFZUDBL5.js.map +1 -0
- package/dist/{chunk-XM25TVIE.js → chunk-XWQCNGTQ.js} +724 -363
- package/dist/chunk-XWQCNGTQ.js.map +1 -0
- package/dist/components.d.ts +5 -5
- package/dist/components.js +14 -11
- package/dist/components.js.map +1 -1
- 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 +8 -12
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +60 -13
- package/dist/index.js +19 -19
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +21 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +145 -114
- package/dist/rbac/index.js +8 -11
- package/dist/theming/runtime.d.ts +1 -13
- package/dist/theming/runtime.js +1 -1
- 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/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +31 -1
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +14 -14
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +7 -1
- package/docs/api/classes/ColumnFactory.md +8 -8
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +18 -15
- 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 +4 -4
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +9 -2
- package/docs/api/interfaces/ButtonProps.md +7 -4
- package/docs/api/interfaces/CalendarProps.md +8 -5
- package/docs/api/interfaces/CardProps.md +8 -5
- 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 +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +24 -21
- package/docs/api/interfaces/DataTableColumn.md +31 -31
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
- package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
- package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +8 -8
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- 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 +26 -23
- package/docs/api/interfaces/FooterProps.md +10 -8
- package/docs/api/interfaces/FormFieldProps.md +10 -10
- 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 +7 -4
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +14 -11
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +11 -11
- package/docs/api/interfaces/NavigationMenuProps.md +15 -15
- 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 +30 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- 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 +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
- package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
- package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
- package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- 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 +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
- 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 +3 -3
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +4 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +14 -11
- package/docs/api/interfaces/UserMenuProps.md +8 -6
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +575 -634
- package/docs/architecture/database-schema-requirements.md +161 -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/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/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/standards/README.md +1 -0
- package/package.json +2 -1
- 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} +714 -687
- 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 +454 -0
- package/scripts/audit/core/checks/documentation.cjs +203 -0
- package/scripts/audit/core/checks/environment.cjs +128 -0
- package/scripts/audit/core/checks/error-handling.cjs +299 -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/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 +61 -849
- 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 +124 -32
- 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 +2 -2
- 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/EventSelector/EventSelector.tsx +3 -0
- package/src/components/FileDisplay/FileDisplay.tsx +32 -18
- 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.tsx +21 -10
- 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/OrganisationSelector/OrganisationSelector.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -2
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +32 -11
- package/src/components/PaceAppLayout/test-setup.tsx +1 -2
- 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 +2 -2
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- 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/useDebounce.ts +9 -0
- package/src/hooks/useEventTheme.ts +6 -0
- package/src/hooks/useFileDisplay.ts +4 -0
- 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 +2 -1
- package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
- package/src/providers/services/AuthServiceProvider.tsx +18 -0
- package/src/providers/services/EventServiceProvider.tsx +18 -0
- package/src/providers/services/InactivityServiceProvider.tsx +18 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +36 -0
- 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 +2 -2
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
- package/src/rbac/adapters.tsx +14 -5
- package/src/rbac/api.ts +100 -67
- package/src/rbac/components/NavigationProvider.tsx +4 -1
- package/src/rbac/components/PagePermissionGuard.tsx +157 -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/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 +347 -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 +71 -64
- package/src/rbac/hooks/usePermissions.ts +14 -995
- package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
- package/src/rbac/hooks/useResourcePermissions.ts +14 -4
- package/src/rbac/hooks/useSecureSupabase.ts +33 -13
- package/src/rbac/permissions.ts +0 -30
- package/src/rbac/secureClient.ts +200 -61
- package/src/rbac/types.ts +8 -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/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-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.map +0 -1
- package/dist/chunk-XM25TVIE.js.map +0 -1
- package/docs/api/classes/ErrorBoundary.md +0 -144
- package/docs/migration/quick-migration-guide.md +0 -356
- package/docs/migration/service-architecture.md +0 -281
- 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-TPTKCX4D.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
- /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.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
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Removed
|
|
11
|
+
- **DataTable Debug Tools Exports**: Removed all public exports from `packages/core/src/components/DataTable/utils/debugTools.ts` as they were unused. The debug tools functionality remains available internally for development but is no longer exported. Removed exports include:
|
|
12
|
+
- `DebugLevel` enum
|
|
13
|
+
- `DebugConfig` interface
|
|
14
|
+
- `DEFAULT_DEBUG_CONFIG` constant
|
|
15
|
+
- `DebugLogEntry` interface
|
|
16
|
+
- `PerformanceTimelineEntry` interface
|
|
17
|
+
- `MemorySnapshot` interface
|
|
18
|
+
- `RenderPerformanceData` interface
|
|
19
|
+
- `DataTableDebugger` class
|
|
20
|
+
- `dataTableDebugger` instance
|
|
21
|
+
- `profile` decorator function
|
|
22
|
+
- `MemoryMonitor` class
|
|
23
|
+
- `usePerformanceDebugger` hook
|
|
24
|
+
- `setupDevTools` function
|
|
25
|
+
- **DataTable Type Exports**: Removed duplicative type exports from `packages/core/src/components/DataTable/types.ts`:
|
|
26
|
+
- `PartialDataRecord<TData>` - Use `Partial<TData>` instead
|
|
27
|
+
- `CompleteDataRecord<TData>` - Use `TData` directly instead
|
|
28
|
+
- **Flexible Import Helper Functions**: Removed unused helper functions from `packages/core/src/components/DataTable/utils/flexibleImport.ts`:
|
|
29
|
+
- `createImportOptions()` - Not used anywhere in the codebase
|
|
30
|
+
- `createRegionalImportOptions()` - Not used anywhere in the codebase
|
|
31
|
+
- `testDateParsing()` - Not used anywhere in the codebase
|
|
32
|
+
|
|
33
|
+
### Migration Notes
|
|
34
|
+
- If you were using `PartialDataRecord` or `CompleteDataRecord`, replace them with `Partial<TData>` and `TData` respectively
|
|
35
|
+
- If you were using any of the debug tools exports, they are no longer available. The debug tools remain functional internally but are not exposed as public APIs
|
|
36
|
+
- If you were using `createImportOptions()`, `createRegionalImportOptions()`, or `testDateParsing()`, you'll need to implement similar functionality yourself or use the `flexibleImport()` function directly with manual options
|
|
37
|
+
|
|
10
38
|
## [0.6.0] - 2025-01-28
|
|
11
39
|
|
|
12
40
|
### Changed
|
|
@@ -14,25 +42,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
14
42
|
- **BREAKING**: Updated peer dependencies to require React ^19.0.0
|
|
15
43
|
- **BREAKING**: Updated @types/react to ^19.2.7 and @types/react-dom to ^19.2.3
|
|
16
44
|
- **BREAKING**: Updated @vitejs/plugin-react to ^5.1.2
|
|
45
|
+
- **BREAKING**: Database schema changes (table naming standardization, person-scoped profiles)
|
|
17
46
|
- Fixed TypeScript errors related to React 19's stricter type system in Button and Select components
|
|
18
47
|
- Updated vitest.config.ts to remove duplicate configuration keys
|
|
19
48
|
|
|
20
49
|
### Added
|
|
21
50
|
- **React Compiler**: Added `babel-plugin-react-compiler` for automatic component optimizations
|
|
22
51
|
- **React Compiler Configuration**: Configured React Compiler in vite.config.ts and vitest.config.ts
|
|
23
|
-
- **Migration
|
|
24
|
-
- Updated documentation to reflect React 19 requirements
|
|
25
|
-
|
|
26
|
-
### Technical Details
|
|
27
|
-
- React Compiler automatically optimizes components during development and in consuming apps
|
|
28
|
-
- TypeScript types updated to handle React 19's stricter `child.props` typing
|
|
29
|
-
- All error boundaries verified compatible with React 19 error handling changes
|
|
30
|
-
- All tests pass with React 19 and React Compiler enabled
|
|
52
|
+
- **Migration Guides**: Added comprehensive migration documentation
|
|
31
53
|
|
|
32
54
|
### Migration Notes
|
|
55
|
+
|
|
56
|
+
**⚠️ CRITICAL**: This version includes major breaking changes. See migration guides for detailed instructions:
|
|
57
|
+
|
|
58
|
+
- **React 19 Upgrade**: See [React 19 Migration Guide](./docs/migration/V0.6.0_REACT_19_MIGRATION.md)
|
|
59
|
+
- **Complete Migration Guide**: See [v0.5.190 → v0.6.1 Migration Guide](./docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md)
|
|
60
|
+
- **Database Changes**: See [Database Changes December 2025](./docs/migration/database-changes-december-2025.md)
|
|
61
|
+
- **Table Naming**: See [Database Changes December 2025](./docs/migration/database-changes-december-2025.md#change-1-table-naming-standardization)
|
|
62
|
+
- **Person-Scoped Profiles**: See [Person-Scoped Profiles Migration Guide](./docs/migration/V0.5.190_person-scoped-profiles-migration-guide.md)
|
|
63
|
+
|
|
64
|
+
**Quick Summary:**
|
|
33
65
|
- Consuming apps must upgrade to React 19.2.3+
|
|
34
|
-
-
|
|
35
|
-
-
|
|
66
|
+
- Database migrations required for table naming and person-scoped profiles
|
|
67
|
+
- All table names updated to follow `app_entity` naming convention
|
|
68
|
+
- Profiles are now person-scoped instead of organisation-scoped
|
|
36
69
|
|
|
37
70
|
## [Unreleased]
|
|
38
71
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Enforce pace-core usage patterns and prevent custom solutions when pace-core provides functionality
|
|
3
3
|
globs: ["src/**/*.{ts,tsx,js,jsx}"]
|
|
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
|
# pace-core Compliance Guide
|
|
9
9
|
|
|
@@ -31,16 +31,8 @@ This guide ensures consuming apps use pace-core components, hooks, and utilities
|
|
|
31
31
|
|
|
32
32
|
**Example:**
|
|
33
33
|
```tsx
|
|
34
|
-
// ❌ WRONG
|
|
35
|
-
|
|
36
|
-
return <button className="btn">Click me</button>;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ✅ CORRECT - Use pace-core
|
|
40
|
-
import { Button } from '@jmruthers/pace-core';
|
|
41
|
-
function MyComponent() {
|
|
42
|
-
return <Button>Click me</Button>;
|
|
43
|
-
}
|
|
34
|
+
// ❌ WRONG: <button className="btn">Click me</button>
|
|
35
|
+
// ✅ CORRECT: import { Button } from '@jmruthers/pace-core'; <Button>Click me</Button>
|
|
44
36
|
```
|
|
45
37
|
|
|
46
38
|
### Hooks
|
|
@@ -59,19 +51,8 @@ function MyComponent() {
|
|
|
59
51
|
|
|
60
52
|
**Example:**
|
|
61
53
|
```tsx
|
|
62
|
-
// ❌ WRONG
|
|
63
|
-
|
|
64
|
-
const [debounced, setDebounced] = useState(value);
|
|
65
|
-
useEffect(() => {
|
|
66
|
-
const timer = setTimeout(() => setDebounced(value), delay);
|
|
67
|
-
return () => clearTimeout(timer);
|
|
68
|
-
}, [value, delay]);
|
|
69
|
-
return debounced;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ✅ CORRECT - Use pace-core
|
|
73
|
-
import { useDebounce } from '@jmruthers/pace-core';
|
|
74
|
-
const debouncedValue = useDebounce(value, 500);
|
|
54
|
+
// ❌ WRONG: Custom useDebounce hook implementation
|
|
55
|
+
// ✅ CORRECT: import { useDebounce } from '@jmruthers/pace-core'; const debouncedValue = useDebounce(value, 500);
|
|
75
56
|
```
|
|
76
57
|
|
|
77
58
|
### Utilities
|
|
@@ -90,14 +71,8 @@ const debouncedValue = useDebounce(value, 500);
|
|
|
90
71
|
|
|
91
72
|
**Example:**
|
|
92
73
|
```tsx
|
|
93
|
-
// ❌ WRONG
|
|
94
|
-
|
|
95
|
-
return new Intl.DateTimeFormat('en-US').format(date);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ✅ CORRECT - Use pace-core
|
|
99
|
-
import { formatDate } from '@jmruthers/pace-core';
|
|
100
|
-
const formatted = formatDate(date);
|
|
74
|
+
// ❌ WRONG: Custom formatDate implementation
|
|
75
|
+
// ✅ CORRECT: import { formatDate } from '@jmruthers/pace-core'; const formatted = formatDate(date);
|
|
101
76
|
```
|
|
102
77
|
|
|
103
78
|
## MUST: Use Secure Supabase Client
|
|
@@ -105,15 +80,8 @@ const formatted = formatDate(date);
|
|
|
105
80
|
**You MUST use `useSecureSupabase()` for all database operations.** Never use the base Supabase client directly.
|
|
106
81
|
|
|
107
82
|
```tsx
|
|
108
|
-
// ❌ WRONG -
|
|
109
|
-
import {
|
|
110
|
-
const supabase = createClient(...);
|
|
111
|
-
await supabase.from('users').select('*');
|
|
112
|
-
|
|
113
|
-
// ✅ CORRECT - Use secure client
|
|
114
|
-
import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
|
|
115
|
-
const secureSupabase = useSecureSupabase();
|
|
116
|
-
await secureSupabase.from('users').select('*');
|
|
83
|
+
// ❌ WRONG: import { createClient } from '@supabase/supabase-js'; const supabase = createClient(...);
|
|
84
|
+
// ✅ CORRECT: import { useSecureSupabase } from '@jmruthers/pace-core/rbac'; const secureSupabase = useSecureSupabase();
|
|
117
85
|
```
|
|
118
86
|
|
|
119
87
|
## MUST: Setup RBAC Before Use
|
|
@@ -124,9 +92,7 @@ await secureSupabase.from('users').select('*');
|
|
|
124
92
|
// main.tsx - MUST be first
|
|
125
93
|
import { setupRBAC } from '@jmruthers/pace-core/rbac';
|
|
126
94
|
setupRBAC(supabase);
|
|
127
|
-
|
|
128
95
|
// Then render app
|
|
129
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
|
|
130
96
|
```
|
|
131
97
|
|
|
132
98
|
## MUST: Read Documentation Before Using Components
|
|
@@ -184,27 +150,13 @@ ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
|
|
|
184
150
|
|
|
185
151
|
**❌ WRONG - Guessing props:**
|
|
186
152
|
```tsx
|
|
187
|
-
|
|
188
|
-
<DataTable
|
|
189
|
-
data={data}
|
|
190
|
-
columns={columns}
|
|
191
|
-
// Missing required rbac prop - will error!
|
|
192
|
-
/>
|
|
153
|
+
<DataTable data={data} columns={columns} /> // Missing required rbac prop
|
|
193
154
|
```
|
|
194
155
|
|
|
195
156
|
**✅ CORRECT - Read documentation first:**
|
|
196
157
|
```tsx
|
|
197
|
-
// 1. Read DataTable docs in implementation-guides/data-tables.md
|
|
198
|
-
// 2. Understand required props (rbac is mandatory)
|
|
199
|
-
// 3. Use correct props
|
|
200
158
|
import { DataTable } from '@jmruthers/pace-core';
|
|
201
|
-
|
|
202
|
-
<DataTable
|
|
203
|
-
data={data}
|
|
204
|
-
columns={columns}
|
|
205
|
-
rbac={{ pageName: 'users' }} // Required - found in docs
|
|
206
|
-
features={{ search: true, pagination: true }}
|
|
207
|
-
/>
|
|
159
|
+
<DataTable data={data} columns={columns} rbac={{ pageName: 'users' }} features={{ search: true }} />
|
|
208
160
|
```
|
|
209
161
|
|
|
210
162
|
### Documentation Checklist
|
|
@@ -249,16 +201,9 @@ Before using any pace-core component:
|
|
|
249
201
|
|
|
250
202
|
```tsx
|
|
251
203
|
import { UnifiedAuthProvider, OrganisationProvider } from '@jmruthers/pace-core';
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
<UnifiedAuthProvider supabaseClient={supabase} appName="Your App">
|
|
256
|
-
<OrganisationProvider>
|
|
257
|
-
{/* Your app */}
|
|
258
|
-
</OrganisationProvider>
|
|
259
|
-
</UnifiedAuthProvider>
|
|
260
|
-
);
|
|
261
|
-
}
|
|
204
|
+
<UnifiedAuthProvider supabaseClient={supabase} appName="Your App">
|
|
205
|
+
<OrganisationProvider>{/* Your app */}</OrganisationProvider>
|
|
206
|
+
</UnifiedAuthProvider>
|
|
262
207
|
```
|
|
263
208
|
|
|
264
209
|
## MUST: Import Core Styles
|
|
@@ -296,26 +241,8 @@ import '@jmruthers/pace-core/styles/core.css';
|
|
|
296
241
|
|
|
297
242
|
**Example:**
|
|
298
243
|
```tsx
|
|
299
|
-
// ❌ WRONG
|
|
300
|
-
<
|
|
301
|
-
Content
|
|
302
|
-
</div>
|
|
303
|
-
|
|
304
|
-
// ❌ WRONG - Inline styles on pace-core component
|
|
305
|
-
<Button style={{ backgroundColor: 'red' }}>Click me</Button>
|
|
306
|
-
|
|
307
|
-
// ✅ CORRECT - Use pace-core component with Tailwind classes
|
|
308
|
-
<Card className="bg-main-500 p-4 text-white">
|
|
309
|
-
<CardContent>Content</CardContent>
|
|
310
|
-
</Card>
|
|
311
|
-
|
|
312
|
-
// ✅ CORRECT - Use pace-core component as-is (already styled)
|
|
313
|
-
<Button variant="default">Click me</Button>
|
|
314
|
-
|
|
315
|
-
// ✅ CORRECT - Use Tailwind for layout/spacing only
|
|
316
|
-
<div className="flex items-center gap-4 p-4">
|
|
317
|
-
<Button>Action</Button>
|
|
318
|
-
</div>
|
|
244
|
+
// ❌ WRONG: <div style={{ backgroundColor: 'blue' }}> or <Button style={{...}}>
|
|
245
|
+
// ✅ CORRECT: <Card className="bg-main-500 p-4"> or <Button variant="default"> or <div className="flex gap-4">
|
|
319
246
|
```
|
|
320
247
|
|
|
321
248
|
### When Tailwind Classes Are Acceptable
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Enforce compliance with all pace-core standards across architecture, API, components, code style, security, testing, and RBAC/RLS
|
|
3
3
|
globs: ["src/**/*.{ts,tsx,js,jsx}", "supabase/migrations/**/*.sql"]
|
|
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
|
# Standards Compliance Guide
|
|
9
9
|
|
|
@@ -26,34 +26,16 @@ This guide ensures consuming apps comply with all pace-core standards. Follow th
|
|
|
26
26
|
**RLS policies MUST use helper functions, NEVER subqueries.**
|
|
27
27
|
|
|
28
28
|
```sql
|
|
29
|
-
-- ❌ WRONG
|
|
30
|
-
CREATE POLICY rbac_select_users ON users
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-- ✅ CORRECT - Helper function
|
|
39
|
-
CREATE OR REPLACE FUNCTION get_user_organisation_ids()
|
|
40
|
-
RETURNS uuid[]
|
|
41
|
-
LANGUAGE plpgsql
|
|
42
|
-
STABLE
|
|
43
|
-
SECURITY DEFINER
|
|
44
|
-
SET search_path TO public
|
|
45
|
-
AS $$
|
|
46
|
-
BEGIN
|
|
47
|
-
RETURN ARRAY(
|
|
48
|
-
SELECT organisation_id
|
|
49
|
-
FROM organisation_memberships
|
|
50
|
-
WHERE user_id = get_effective_user_id()
|
|
51
|
-
);
|
|
52
|
-
END;
|
|
29
|
+
-- ❌ WRONG: Subquery in RLS policy (causes N+1 queries)
|
|
30
|
+
CREATE POLICY rbac_select_users ON users FOR SELECT USING (
|
|
31
|
+
organisation_id IN (SELECT organisation_id FROM organisation_memberships WHERE user_id = auth.uid())
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
-- ✅ CORRECT: Helper function with STABLE SECURITY DEFINER
|
|
35
|
+
CREATE OR REPLACE FUNCTION get_user_organisation_ids() RETURNS uuid[] LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO public AS $$
|
|
36
|
+
BEGIN RETURN ARRAY(SELECT organisation_id FROM organisation_memberships WHERE user_id = get_effective_user_id()); END;
|
|
53
37
|
$$;
|
|
54
|
-
|
|
55
|
-
CREATE POLICY rbac_select_users ON users
|
|
56
|
-
FOR SELECT USING (organisation_id = ANY(get_user_organisation_ids()));
|
|
38
|
+
CREATE POLICY rbac_select_users ON users FOR SELECT USING (organisation_id = ANY(get_user_organisation_ids()));
|
|
57
39
|
```
|
|
58
40
|
|
|
59
41
|
### MUST: Test Database Migrations
|
|
@@ -72,14 +54,8 @@ CREATE POLICY rbac_select_users ON users
|
|
|
72
54
|
- Bulk operations: `_bulk` suffix (e.g., `app_cake_dish_create_bulk`)
|
|
73
55
|
|
|
74
56
|
```sql
|
|
75
|
-
-- ✅ CORRECT
|
|
76
|
-
|
|
77
|
-
CREATE FUNCTION app_cake_dish_create(...)
|
|
78
|
-
CREATE FUNCTION app_cake_dish_create_bulk(...)
|
|
79
|
-
|
|
80
|
-
-- ❌ WRONG
|
|
81
|
-
CREATE FUNCTION getDishes(...)
|
|
82
|
-
CREATE FUNCTION create_dish(...)
|
|
57
|
+
-- ✅ CORRECT: data_cake_dishes_list, app_cake_dish_create, app_cake_dish_create_bulk
|
|
58
|
+
-- ❌ WRONG: getDishes, create_dish (wrong naming pattern)
|
|
83
59
|
```
|
|
84
60
|
|
|
85
61
|
### MUST: Use ApiResult Shape
|
|
@@ -87,15 +63,8 @@ CREATE FUNCTION create_dish(...)
|
|
|
87
63
|
**All RPCs MUST return ApiResult shape:**
|
|
88
64
|
|
|
89
65
|
```typescript
|
|
90
|
-
type ApiResult<T> =
|
|
91
|
-
|
|
92
|
-
| { ok: false; error: ApiError };
|
|
93
|
-
|
|
94
|
-
type ApiError = {
|
|
95
|
-
code: string;
|
|
96
|
-
message: string;
|
|
97
|
-
details?: object;
|
|
98
|
-
};
|
|
66
|
+
type ApiResult<T> = { ok: true; data: T } | { ok: false; error: ApiError };
|
|
67
|
+
type ApiError = { code: string; message: string; details?: object };
|
|
99
68
|
```
|
|
100
69
|
|
|
101
70
|
### MUST: Enforce RLS in RPCs
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Define standard folder structure and file organization for consuming apps
|
|
3
|
-
globs: ["**/*.{ts,tsx,js,jsx,md}"]
|
|
4
|
-
alwaysApply:
|
|
5
|
-
paceCoreVersion: "0.
|
|
6
|
-
rulesVersion: "2025-01-
|
|
3
|
+
globs: ["**/*.{ts,tsx,js,jsx,md}", "src/**"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
paceCoreVersion: "0.6.x"
|
|
6
|
+
rulesVersion: "2025-01-28"
|
|
7
7
|
---
|
|
8
8
|
# Project Structure Standard
|
|
9
9
|
|
|
@@ -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
|