@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
|
@@ -427,7 +427,7 @@ const { data: files } = await supabase
|
|
|
427
427
|
|
|
428
428
|
**Migration:** `20251205211120_migrate_profiles_to_person_scoped.sql` + related migrations
|
|
429
429
|
**Date:** 2025-12-05
|
|
430
|
-
**Documentation:** See [Person-Scoped Profiles Migration Guide](./
|
|
430
|
+
**Documentation:** See [Person-Scoped Profiles Migration Guide](./V0.5.190_person-scoped-profiles-migration-guide.md)
|
|
431
431
|
|
|
432
432
|
### Quick Summary
|
|
433
433
|
|
|
@@ -501,7 +501,7 @@ const { data } = await supabase
|
|
|
501
501
|
- [ ] Update `pace_contact` queries to use `person_id` instead of `member_id`
|
|
502
502
|
- [ ] Update profile creation logic (no `organisation_id` needed)
|
|
503
503
|
- [ ] Review profile access patterns (person-scoped, not org-scoped)
|
|
504
|
-
- [ ] See [Person-Scoped Profiles Migration Guide](./
|
|
504
|
+
- [ ] See [Person-Scoped Profiles Migration Guide](./V0.5.190_person-scoped-profiles-migration-guide.md) for detailed checklist
|
|
505
505
|
|
|
506
506
|
### Phase 3: Testing
|
|
507
507
|
|
|
@@ -708,7 +708,7 @@ All migration files are located in:
|
|
|
708
708
|
|
|
709
709
|
### Documentation
|
|
710
710
|
|
|
711
|
-
- [Person-Scoped Profiles Migration Guide](./
|
|
711
|
+
- [Person-Scoped Profiles Migration Guide](./V0.5.190_person-scoped-profiles-migration-guide.md)
|
|
712
712
|
- [API & RPC Standard](../standards/02-api-and-rpc-standard.md)
|
|
713
713
|
- [Database Schema Requirements](../architecture/database-schema-requirements.md)
|
|
714
714
|
|
|
@@ -973,7 +973,7 @@ VALUES (
|
|
|
973
973
|
- **[API Reference](./api-reference.md)** - Complete API documentation
|
|
974
974
|
- **[Examples](./examples.md)** - More complex usage patterns
|
|
975
975
|
- **[Troubleshooting](./troubleshooting.md)** - Advanced troubleshooting
|
|
976
|
-
- **[Migration Guide](../migration/
|
|
976
|
+
- **[Migration Guide](../migration/V0.4.0_rbac-migration.md)** - If migrating from legacy RBAC
|
|
977
977
|
|
|
978
978
|
## 🆘 Still Having Issues?
|
|
979
979
|
|
|
@@ -843,7 +843,7 @@ function AdaptiveComponent({ appId }) {
|
|
|
843
843
|
- [Examples](./examples.md) - More complex examples
|
|
844
844
|
- [Event-Based Apps](./event-based-apps.md) - Guide for event-based applications
|
|
845
845
|
- [Troubleshooting](./troubleshooting.md) - Common issues and solutions
|
|
846
|
-
- [Migration Guide](../migration/
|
|
846
|
+
- [Migration Guide](../migration/V0.4.0_rbac-migration.md) - Migrating from legacy RBAC
|
|
847
847
|
|
|
848
848
|
## ♿ Accessibility
|
|
849
849
|
|
package/docs/rbac/quick-start.md
CHANGED
|
@@ -740,7 +740,7 @@ The system will automatically resolve the organisation ID from the event context
|
|
|
740
740
|
- **[API Reference](./api-reference.md)** - Complete API documentation
|
|
741
741
|
- **[Examples](./examples.md)** - More complex usage patterns
|
|
742
742
|
- **[Troubleshooting](./troubleshooting.md)** - Advanced troubleshooting
|
|
743
|
-
- **[Migration Guide](../migration/
|
|
743
|
+
- **[Migration Guide](../migration/V0.4.0_rbac-migration.md)** - If migrating from legacy RBAC
|
|
744
744
|
|
|
745
745
|
## 🆘 Still Having Issues?
|
|
746
746
|
|
package/docs/standards/README.md
CHANGED
|
@@ -57,6 +57,7 @@ The cursor rules cover:
|
|
|
57
57
|
- **05-bug-reports-and-features** - Templates for issue reporting
|
|
58
58
|
- **06-code-quality** - Enforce code quality standards
|
|
59
59
|
- **07-tech-stack-compliance** - Enforce tech stack versions
|
|
60
|
+
- **08-markup-quality** - Enforce clean markup standards, semantic HTML usage, and pace-core component patterns
|
|
60
61
|
|
|
61
62
|
### Audit Tool
|
|
62
63
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jmruthers/pace-core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"./core-usage-manifest.json": "./core-usage-manifest.json",
|
|
74
74
|
"./cursor-rules": "./cursor-rules",
|
|
75
75
|
"./scripts/install-cursor-rules": "./scripts/install-cursor-rules.cjs",
|
|
76
|
+
"./scripts/audit": "./scripts/audit/index.cjs",
|
|
76
77
|
"./scripts/audit-consuming-app": "./scripts/audit-consuming-app.cjs",
|
|
77
78
|
"./source": {
|
|
78
79
|
"import": "./src/index.ts",
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Accessibility Check Module
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Audit/Checks/Accessibility
|
|
7
|
+
*
|
|
8
|
+
* Checks for:
|
|
9
|
+
* - Missing aria-* attributes
|
|
10
|
+
* - Missing keyboard navigation
|
|
11
|
+
* - Missing focus management
|
|
12
|
+
* - Missing alt text on images
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const { getRelativePath, getLineNumber } = require('../utils.cjs');
|
|
17
|
+
|
|
18
|
+
const accessibilityCheck = {
|
|
19
|
+
name: 'accessibility',
|
|
20
|
+
description: 'Accessibility checks (aria attributes, keyboard navigation, alt text)',
|
|
21
|
+
severity: 'warning',
|
|
22
|
+
|
|
23
|
+
async run(context) {
|
|
24
|
+
const { projectRoot, files } = context;
|
|
25
|
+
const issues = [];
|
|
26
|
+
const warnings = [];
|
|
27
|
+
const suggestions = [];
|
|
28
|
+
|
|
29
|
+
if (!files || files.length === 0) {
|
|
30
|
+
return { issues, warnings, suggestions };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const filePath of files) {
|
|
34
|
+
try {
|
|
35
|
+
// Only check React component files
|
|
36
|
+
if (!filePath.match(/\.(tsx|jsx)$/)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
41
|
+
const relativePath = getRelativePath(filePath, projectRoot);
|
|
42
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
43
|
+
|
|
44
|
+
// Skip root-level src directory - in pace-core repository, this is a demo/showcase app
|
|
45
|
+
// Note: We DO check packages/core/ files because accessibility issues (missing alt attributes, missing form labels) are real issues that should be fixed
|
|
46
|
+
const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
|
|
47
|
+
if (isRootSrc) {
|
|
48
|
+
continue; // Skip demo app files
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Skip scripts directory - utility scripts don't need accessibility validation
|
|
52
|
+
const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
|
|
53
|
+
if (isScript) {
|
|
54
|
+
continue; // Skip script files
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Skip pace-core library components and examples - these are designed to accept accessibility props
|
|
58
|
+
// Library components accept id, aria-label, etc. via props spread
|
|
59
|
+
// Examples are demonstration code, not production code
|
|
60
|
+
const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
|
|
61
|
+
const isExample = normalizedPath.includes('/examples/');
|
|
62
|
+
const isLibraryComponent = isPaceCorePackage && !isExample;
|
|
63
|
+
|
|
64
|
+
// Check for images without alt text
|
|
65
|
+
// Skip library components - they accept alt as a prop
|
|
66
|
+
if (!isLibraryComponent) {
|
|
67
|
+
const imgPattern = /<img[^>]*>/g;
|
|
68
|
+
let imgMatch;
|
|
69
|
+
while ((imgMatch = imgPattern.exec(content)) !== null) {
|
|
70
|
+
const imgTag = imgMatch[0];
|
|
71
|
+
// Check for alt attribute, including React props (alt=, alt =, alt={)
|
|
72
|
+
const hasAlt = imgTag.includes('alt=') || imgTag.includes('alt =') || imgTag.includes('alt={');
|
|
73
|
+
if (!hasAlt) {
|
|
74
|
+
warnings.push({
|
|
75
|
+
type: 'missing-alt-text',
|
|
76
|
+
file: relativePath,
|
|
77
|
+
line: getLineNumber(content, imgMatch.index),
|
|
78
|
+
message: 'Image element missing alt attribute',
|
|
79
|
+
recommendation: 'Add alt text to all images for accessibility: <img alt="description" ... />'
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check for buttons/clickable elements without aria-label or accessible text
|
|
86
|
+
const buttonPattern = /<(button|a|div)\s+[^>]*(onClick|role=["']button["'])[^>]*>/g;
|
|
87
|
+
let buttonMatch;
|
|
88
|
+
while ((buttonMatch = buttonPattern.exec(content)) !== null) {
|
|
89
|
+
const buttonTag = buttonMatch[0];
|
|
90
|
+
const hasAriaLabel = buttonTag.includes('aria-label=') || buttonTag.includes('ariaLabel=');
|
|
91
|
+
const hasAccessibleText = buttonTag.includes('>') && content.substring(buttonMatch.index).match(/<[^>]*>([^<]+)</);
|
|
92
|
+
|
|
93
|
+
if (!hasAriaLabel && !hasAccessibleText) {
|
|
94
|
+
warnings.push({
|
|
95
|
+
type: 'missing-aria-label',
|
|
96
|
+
file: relativePath,
|
|
97
|
+
line: getLineNumber(content, buttonMatch.index),
|
|
98
|
+
message: 'Interactive element missing accessible label',
|
|
99
|
+
recommendation: 'Add aria-label or ensure element contains accessible text content'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check for form inputs without labels
|
|
105
|
+
// Skip library components - they accept id, aria-label, etc. via props spread
|
|
106
|
+
// Skip examples - they're demonstration code
|
|
107
|
+
if (!isLibraryComponent && !isExample) {
|
|
108
|
+
const inputPattern = /<input[^>]*>/g;
|
|
109
|
+
let inputMatch;
|
|
110
|
+
while ((inputMatch = inputPattern.exec(content)) !== null) {
|
|
111
|
+
// Get the full input tag including multi-line attributes
|
|
112
|
+
const inputStart = inputMatch.index;
|
|
113
|
+
const afterInput = content.substring(inputStart, Math.min(content.length, inputStart + 500));
|
|
114
|
+
const inputTagEnd = afterInput.indexOf('>');
|
|
115
|
+
const fullInputTag = inputTagEnd !== -1 ? afterInput.substring(0, inputTagEnd + 1) : inputMatch[0];
|
|
116
|
+
|
|
117
|
+
// Check for id (including React props)
|
|
118
|
+
const hasId = /id=["']([^"']+)["']|id=\{/.test(fullInputTag);
|
|
119
|
+
// Check for aria-label (including React props)
|
|
120
|
+
const hasAriaLabel = /aria-label=["']|ariaLabel=|aria-label=\{/.test(fullInputTag);
|
|
121
|
+
|
|
122
|
+
if (hasId) {
|
|
123
|
+
const idMatch = fullInputTag.match(/id=["']([^"']+)["']/);
|
|
124
|
+
if (idMatch) {
|
|
125
|
+
const id = idMatch[1];
|
|
126
|
+
// Check if there's a corresponding label
|
|
127
|
+
const beforeInput = content.substring(Math.max(0, inputMatch.index - 200), inputMatch.index);
|
|
128
|
+
const hasLabel = new RegExp(`<label[^>]*for=["']${id}["']`, 'i').test(beforeInput);
|
|
129
|
+
|
|
130
|
+
if (!hasLabel && !hasAriaLabel) {
|
|
131
|
+
warnings.push({
|
|
132
|
+
type: 'missing-input-label',
|
|
133
|
+
file: relativePath,
|
|
134
|
+
line: getLineNumber(content, inputMatch.index),
|
|
135
|
+
message: 'Form input missing associated label',
|
|
136
|
+
recommendation: 'Add a <label> element with for attribute matching input id, or use aria-label'
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else if (!hasAriaLabel) {
|
|
141
|
+
warnings.push({
|
|
142
|
+
type: 'missing-input-label',
|
|
143
|
+
file: relativePath,
|
|
144
|
+
line: getLineNumber(content, inputMatch.index),
|
|
145
|
+
message: 'Form input missing id and label',
|
|
146
|
+
recommendation: 'Add id to input and corresponding label, or use aria-label'
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Check for missing focus management in modals/dialogs
|
|
153
|
+
const dialogPattern = /<Dialog|<dialog/gi;
|
|
154
|
+
if (dialogPattern.test(content)) {
|
|
155
|
+
// Skip Dialog.tsx itself - it IS the Dialog component
|
|
156
|
+
if (normalizedPath.includes('/Dialog/Dialog.tsx') || normalizedPath.includes('/Dialog/Dialog.jsx')) {
|
|
157
|
+
continue; // Skip the Dialog component file itself
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check if Dialog from pace-core is used (which handles focus automatically)
|
|
161
|
+
// Check for both published package import and relative imports (within pace-core repo)
|
|
162
|
+
const usesPaceCoreDialog = content.includes('from \'@jmruthers/pace-core\'') ||
|
|
163
|
+
content.includes('from "@jmruthers/pace-core"') ||
|
|
164
|
+
content.includes('from \'../Dialog') ||
|
|
165
|
+
content.includes('from "../Dialog') ||
|
|
166
|
+
content.includes('from \'../../Dialog') ||
|
|
167
|
+
content.includes('from "../../Dialog') ||
|
|
168
|
+
content.includes('from \'../../../Dialog') ||
|
|
169
|
+
content.includes('from "../../../Dialog') ||
|
|
170
|
+
content.includes('from \'./Dialog') ||
|
|
171
|
+
content.includes('from "./Dialog');
|
|
172
|
+
|
|
173
|
+
if (!usesPaceCoreDialog) {
|
|
174
|
+
suggestions.push({
|
|
175
|
+
type: 'dialog-focus-management',
|
|
176
|
+
file: relativePath,
|
|
177
|
+
message: 'Custom dialog implementation detected',
|
|
178
|
+
recommendation: 'Use Dialog component from @jmruthers/pace-core which handles focus management automatically'
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check for color contrast issues (heuristic - check for color props without sufficient contrast)
|
|
184
|
+
const colorPattern = /(?:color|bg-|text-)=["']([^"']+)["']/g;
|
|
185
|
+
// This is a simplified check - full implementation would need color contrast calculation
|
|
186
|
+
// For now, just suggest using pace-core components which handle contrast
|
|
187
|
+
|
|
188
|
+
} catch (error) {
|
|
189
|
+
// Skip files with errors
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return { issues, warnings, suggestions };
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
module.exports = accessibilityCheck;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* API Usage Patterns Check Module
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Audit/Checks/ApiUsage
|
|
7
|
+
*
|
|
8
|
+
* Checks for:
|
|
9
|
+
* - Incorrect usage of pace-core hooks
|
|
10
|
+
* - Missing loading states
|
|
11
|
+
* - Missing error states
|
|
12
|
+
* - Race conditions in async operations
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const { getRelativePath, getLineNumber } = require('../utils.cjs');
|
|
17
|
+
|
|
18
|
+
const apiUsageCheck = {
|
|
19
|
+
name: 'api-usage',
|
|
20
|
+
description: 'API usage patterns (hook usage, loading/error states)',
|
|
21
|
+
severity: 'warning',
|
|
22
|
+
|
|
23
|
+
async run(context) {
|
|
24
|
+
const { projectRoot, files } = context;
|
|
25
|
+
const issues = [];
|
|
26
|
+
const warnings = [];
|
|
27
|
+
const suggestions = [];
|
|
28
|
+
|
|
29
|
+
if (!files || files.length === 0) {
|
|
30
|
+
return { issues, warnings, suggestions };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// pace-core hooks that return loading/error states
|
|
34
|
+
const asyncHooks = {
|
|
35
|
+
'useUnifiedAuth': ['loading', 'error'],
|
|
36
|
+
'useOrganisations': ['isLoading', 'error'],
|
|
37
|
+
'useEvents': ['isLoading', 'error'],
|
|
38
|
+
'useSecureSupabase': [],
|
|
39
|
+
'useFileReference': ['isLoading', 'error'],
|
|
40
|
+
'useRBAC': ['isLoading', 'error']
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for (const filePath of files) {
|
|
44
|
+
try {
|
|
45
|
+
// Only check React component files
|
|
46
|
+
if (!filePath.match(/\.(tsx|jsx)$/)) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
51
|
+
const relativePath = getRelativePath(filePath, projectRoot);
|
|
52
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
53
|
+
|
|
54
|
+
// Skip pace-core package files - api-usage check is for consuming applications, not the library itself
|
|
55
|
+
// Note: Library components may intentionally not check loading/error states in all cases
|
|
56
|
+
// (e.g., internal components that assume context is already set up)
|
|
57
|
+
const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
|
|
58
|
+
if (isPaceCorePackage) {
|
|
59
|
+
continue; // Skip library files (including examples)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Skip root-level src directory - in pace-core repository, this is a demo/showcase app
|
|
63
|
+
const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
|
|
64
|
+
if (isRootSrc) {
|
|
65
|
+
continue; // Skip demo app files
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Skip scripts directory - utility scripts don't need API usage validation
|
|
69
|
+
const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
|
|
70
|
+
if (isScript) {
|
|
71
|
+
continue; // Skip script files
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Check each async hook
|
|
75
|
+
Object.entries(asyncHooks).forEach(([hookName, expectedStates]) => {
|
|
76
|
+
if (!content.includes(hookName)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const hookPattern = new RegExp(`const\\s+[^=]+=\\s+${hookName}\\s*\\(`, 'g');
|
|
81
|
+
let hookMatch;
|
|
82
|
+
while ((hookMatch = hookPattern.exec(content)) !== null) {
|
|
83
|
+
// Check the entire component, not just 300 chars after hook
|
|
84
|
+
// React's Rules of Hooks require all hooks to be called before conditional returns,
|
|
85
|
+
// so state checks may occur later in the component
|
|
86
|
+
const hookLine = getLineNumber(content, hookMatch.index);
|
|
87
|
+
const lines = content.split('\n');
|
|
88
|
+
const hookLineIndex = hookLine - 1;
|
|
89
|
+
|
|
90
|
+
// Get the rest of the component after this hook
|
|
91
|
+
const afterHookContent = lines.slice(hookLineIndex).join('\n');
|
|
92
|
+
|
|
93
|
+
// Check if loading state is used
|
|
94
|
+
if (expectedStates.includes('loading') || expectedStates.includes('isLoading')) {
|
|
95
|
+
const loadingState = expectedStates.includes('loading') ? 'loading' : 'isLoading';
|
|
96
|
+
// Check for loading state usage in the entire component
|
|
97
|
+
// Also check for common patterns: isLoading, authLoading, etc.
|
|
98
|
+
const loadingPattern = new RegExp(`\\b(${loadingState}|authLoading|rbacLoading|orgLoading)\\b`);
|
|
99
|
+
if (!loadingPattern.test(afterHookContent)) {
|
|
100
|
+
warnings.push({
|
|
101
|
+
type: 'missing-loading-state',
|
|
102
|
+
file: relativePath,
|
|
103
|
+
line: hookLine,
|
|
104
|
+
message: `${hookName} returns loading state but it's not being checked`,
|
|
105
|
+
recommendation: `Check ${loadingState} state and show loading UI while data is fetching. You can check loading state after all hooks are called (required by React's Rules of Hooks).`
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check if error state is used
|
|
111
|
+
if (expectedStates.includes('error')) {
|
|
112
|
+
// Check for error state usage in the entire component
|
|
113
|
+
// Also check for common patterns: error, authError, orgError, rbacError
|
|
114
|
+
// Also check for null checks for useSecureSupabase
|
|
115
|
+
const errorPattern = /\b(error|authError|orgError|rbacError)\b/;
|
|
116
|
+
const hasNullCheck = hookName === 'useSecureSupabase' && /secureSupabase\s*===?\s*null|!secureSupabase/.test(afterHookContent);
|
|
117
|
+
|
|
118
|
+
if (!errorPattern.test(afterHookContent) && !hasNullCheck) {
|
|
119
|
+
warnings.push({
|
|
120
|
+
type: 'missing-error-state',
|
|
121
|
+
file: relativePath,
|
|
122
|
+
line: hookLine,
|
|
123
|
+
message: `${hookName} returns error state but it's not being checked`,
|
|
124
|
+
recommendation: 'Check error state and handle errors appropriately. You can check error state after all hooks are called (required by React\'s Rules of Hooks), or handle errors via useEffect hooks. For useSecureSupabase, null checks are also valid error handling.'
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Check for race conditions in async operations
|
|
132
|
+
const asyncOperationPattern = /(?:const|let)\s+(\w+)\s*=\s*(?:await\s+)?(\w+)\.(from|rpc|select)/g;
|
|
133
|
+
let asyncMatch;
|
|
134
|
+
while ((asyncMatch = asyncOperationPattern.exec(content)) !== null) {
|
|
135
|
+
const varName = asyncMatch[1];
|
|
136
|
+
const operation = asyncMatch[0];
|
|
137
|
+
|
|
138
|
+
// Check if this is in a useEffect without cleanup
|
|
139
|
+
const beforeMatch = content.substring(Math.max(0, asyncMatch.index - 500), asyncMatch.index);
|
|
140
|
+
const isInUseEffect = /useEffect\s*\(/.test(beforeMatch);
|
|
141
|
+
|
|
142
|
+
if (isInUseEffect) {
|
|
143
|
+
// Check if there's cleanup/abort
|
|
144
|
+
const afterMatch = content.substring(asyncMatch.index, Math.min(content.length, asyncMatch.index + 500));
|
|
145
|
+
const hasCleanup = /return\s+\(\)\s*=>|AbortController|signal/.test(afterMatch);
|
|
146
|
+
|
|
147
|
+
if (!hasCleanup) {
|
|
148
|
+
suggestions.push({
|
|
149
|
+
type: 'potential-race-condition',
|
|
150
|
+
file: relativePath,
|
|
151
|
+
line: getLineNumber(content, asyncMatch.index),
|
|
152
|
+
message: 'Async operation in useEffect without cleanup - potential race condition',
|
|
153
|
+
recommendation: 'Add cleanup function to cancel requests when component unmounts or dependencies change'
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check for missing request cancellation
|
|
160
|
+
const fetchPattern = /fetch\s*\(|axios\.(get|post|put|delete)\s*\(/g;
|
|
161
|
+
let fetchMatch;
|
|
162
|
+
while ((fetchMatch = fetchPattern.exec(content)) !== null) {
|
|
163
|
+
const beforeMatch = content.substring(Math.max(0, fetchMatch.index - 200), fetchMatch.index);
|
|
164
|
+
const isInUseEffect = /useEffect\s*\(/.test(beforeMatch);
|
|
165
|
+
|
|
166
|
+
if (isInUseEffect) {
|
|
167
|
+
const afterMatch = content.substring(fetchMatch.index, Math.min(content.length, fetchMatch.index + 500));
|
|
168
|
+
const hasAbort = /AbortController|signal|abort/.test(afterMatch);
|
|
169
|
+
|
|
170
|
+
if (!hasAbort) {
|
|
171
|
+
suggestions.push({
|
|
172
|
+
type: 'missing-abort',
|
|
173
|
+
file: relativePath,
|
|
174
|
+
line: getLineNumber(content, fetchMatch.index),
|
|
175
|
+
message: 'Fetch/axios request without abort signal',
|
|
176
|
+
recommendation: 'Use AbortController to cancel requests when component unmounts'
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
} catch (error) {
|
|
183
|
+
// Skip files with errors
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return { issues, warnings, suggestions };
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
module.exports = apiUsageCheck;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bundle Analysis Check Module
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Audit/Checks/Bundle
|
|
7
|
+
*
|
|
8
|
+
* Checks for:
|
|
9
|
+
* - Large bundle sizes
|
|
10
|
+
* - Duplicate dependencies
|
|
11
|
+
* - Unused code imports
|
|
12
|
+
* - Missing code splitting opportunities
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const { getRelativePath } = require('../utils.cjs');
|
|
18
|
+
|
|
19
|
+
const bundleCheck = {
|
|
20
|
+
name: 'bundle',
|
|
21
|
+
description: 'Bundle analysis (size, duplicates, code splitting)',
|
|
22
|
+
severity: 'suggestion',
|
|
23
|
+
|
|
24
|
+
async run(context) {
|
|
25
|
+
const { projectRoot } = context;
|
|
26
|
+
const issues = [];
|
|
27
|
+
const warnings = [];
|
|
28
|
+
const suggestions = [];
|
|
29
|
+
|
|
30
|
+
// Skip if this is the pace-core repository itself
|
|
31
|
+
// Detect pace-core repository by checking if packages/core exists
|
|
32
|
+
// Bundle analysis is more relevant for consuming applications, not the library itself
|
|
33
|
+
const packagesCorePath = path.join(projectRoot, 'packages', 'core');
|
|
34
|
+
const isPaceCoreRepository = fs.existsSync(packagesCorePath);
|
|
35
|
+
|
|
36
|
+
// Check for bundle analysis reports
|
|
37
|
+
const bundleReportPaths = [
|
|
38
|
+
path.join(projectRoot, 'dist', 'stats.html'),
|
|
39
|
+
path.join(projectRoot, 'dist', 'bundle-analysis.json'),
|
|
40
|
+
path.join(projectRoot, '.vite', 'dist', 'stats.html')
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
let hasBundleReport = false;
|
|
44
|
+
for (const reportPath of bundleReportPaths) {
|
|
45
|
+
if (fs.existsSync(reportPath)) {
|
|
46
|
+
hasBundleReport = true;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Skip bundle analysis suggestion if this is the pace-core repository
|
|
52
|
+
// The library itself doesn't need bundle analysis in the same way as consuming apps
|
|
53
|
+
if (!hasBundleReport && !isPaceCoreRepository) {
|
|
54
|
+
suggestions.push({
|
|
55
|
+
type: 'missing-bundle-analysis',
|
|
56
|
+
file: 'package.json',
|
|
57
|
+
message: 'No bundle analysis report found',
|
|
58
|
+
recommendation: 'Run bundle analysis to identify optimization opportunities: npm run build -- --analyze or use vite-bundle-visualizer'
|
|
59
|
+
});
|
|
60
|
+
return { issues, warnings, suggestions };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check dist folder size
|
|
64
|
+
const distPath = path.join(projectRoot, 'dist');
|
|
65
|
+
if (fs.existsSync(distPath)) {
|
|
66
|
+
try {
|
|
67
|
+
const distSize = getDirectorySize(distPath);
|
|
68
|
+
const sizeMB = distSize / (1024 * 1024);
|
|
69
|
+
|
|
70
|
+
if (sizeMB > 5) {
|
|
71
|
+
warnings.push({
|
|
72
|
+
type: 'large-bundle',
|
|
73
|
+
file: 'dist/',
|
|
74
|
+
message: `Bundle size is ${sizeMB.toFixed(2)}MB, which is quite large`,
|
|
75
|
+
recommendation: 'Consider code splitting, lazy loading, and removing unused dependencies'
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
// Skip if can't calculate size
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check for duplicate imports (same package imported multiple ways)
|
|
84
|
+
// This is a simplified check - full implementation would analyze actual bundle
|
|
85
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
86
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
87
|
+
try {
|
|
88
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
89
|
+
const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
90
|
+
|
|
91
|
+
// Check for potential duplicates (e.g., lodash and lodash-es)
|
|
92
|
+
// Note: '@supabase/supabase-js' (client library) and 'supabase' (CLI tool) are NOT duplicates
|
|
93
|
+
// - '@supabase/supabase-js' is the JavaScript client library (runtime dependency)
|
|
94
|
+
// - 'supabase' is the Supabase CLI tool (dev dependency for migrations, local dev)
|
|
95
|
+
// The CLI tool is not bundled in the application, so having both is correct
|
|
96
|
+
const duplicatePatterns = [
|
|
97
|
+
['lodash', 'lodash-es'],
|
|
98
|
+
['date-fns', 'date-fns-tz']
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
duplicatePatterns.forEach(([dep1, dep2]) => {
|
|
102
|
+
if (allDeps[dep1] && allDeps[dep2]) {
|
|
103
|
+
warnings.push({
|
|
104
|
+
type: 'duplicate-dependency',
|
|
105
|
+
file: 'package.json',
|
|
106
|
+
message: `Both '${dep1}' and '${dep2}' are installed - may cause duplicate code in bundle`,
|
|
107
|
+
recommendation: `Remove one and use pace-core alternatives if available, or ensure only one is bundled`
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
} catch (error) {
|
|
112
|
+
// Skip if can't parse package.json
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { issues, warnings, suggestions };
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Calculate directory size recursively
|
|
122
|
+
*/
|
|
123
|
+
function getDirectorySize(dirPath) {
|
|
124
|
+
let size = 0;
|
|
125
|
+
try {
|
|
126
|
+
const items = fs.readdirSync(dirPath);
|
|
127
|
+
items.forEach(item => {
|
|
128
|
+
const itemPath = path.join(dirPath, item);
|
|
129
|
+
const stat = fs.statSync(itemPath);
|
|
130
|
+
if (stat.isDirectory()) {
|
|
131
|
+
size += getDirectorySize(itemPath);
|
|
132
|
+
} else {
|
|
133
|
+
size += stat.size;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
} catch (error) {
|
|
137
|
+
// Skip if can't read
|
|
138
|
+
}
|
|
139
|
+
return size;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = bundleCheck;
|