@jmruthers/pace-core 0.5.193 → 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 +62 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +299 -0
- package/cursor-rules/01-standards-compliance.mdc +244 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +222 -0
- package/cursor-rules/04-testing-standards.mdc +268 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +309 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +119 -0
- package/cursor-rules/README.md +192 -0
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
- package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
- package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
- package/dist/{UnifiedAuthProvider-RGJTDE2C.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-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
- package/dist/chunk-2UOI2FG5.js.map +1 -0
- package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
- package/dist/chunk-3XC4CPTD.js.map +1 -0
- package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
- package/dist/chunk-6J4GEEJR.js.map +1 -0
- package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
- 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-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
- 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-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
- package/dist/chunk-MMZ7JXPU.js.map +1 -0
- package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
- package/dist/chunk-NECFR5MM.js.map +1 -0
- package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
- package/dist/chunk-SFZUDBL5.js.map +1 -0
- package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
- package/dist/chunk-XWQCNGTQ.js.map +1 -0
- package/dist/components.d.ts +6 -6
- package/dist/components.js +15 -12
- 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 +59 -126
- package/dist/hooks.js +19 -28
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +63 -16
- package/dist/index.js +23 -24
- 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 +146 -115
- 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-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +15 -15
- 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 +263 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +6 -28
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -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 +40 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +12 -6
- 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} +737 -691
- 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 +86 -0
- 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/install-cursor-rules.cjs +236 -0
- 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__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- 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 +16 -4
- package/src/components/Button/Button.tsx +27 -4
- package/src/components/Calendar/Calendar.tsx +9 -3
- 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.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +40 -6
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
- 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 +200 -561
- 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 +9 -1
- 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 +9 -1
- 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 +62 -852
- 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/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/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 +14 -2
- 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/useKeyboardNavigation.ts +2 -2
- 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/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +8 -7
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/EventSelector/EventSelector.tsx +4 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
- 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 +15 -12
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +31 -26
- package/src/components/Header/Header.tsx +22 -11
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +36 -34
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +12 -8
- package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
- 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.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceAppLayout/test-setup.tsx +1 -2
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +95 -438
- 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 +5 -6
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +5 -1
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +22 -19
- package/src/components/index.ts +2 -2
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +5 -1
- package/src/hooks/public/usePublicEventLogo.ts +5 -1
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +5 -1
- 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/useDataTableState.ts +8 -18
- 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 +16 -2
- package/src/hooks/useFocusTrap.ts +7 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +4 -1
- 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 +8 -1
- 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 +3 -3
- package/src/index.ts +2 -1
- package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- 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 +58 -22
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
- 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/EnhancedNavigationMenu.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.tsx +5 -2
- package/src/rbac/components/PagePermissionGuard.tsx +158 -18
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +6 -2
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +21 -6
- 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 +212 -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/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js +0 -628
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js.map +0 -1
- package/dist/chunk-7FLMSG37.js 2.map +0 -1
- package/dist/chunk-7FLMSG37.js.map +0 -1
- package/dist/chunk-BC4IJKSL.js.map +0 -1
- package/dist/chunk-E3SPN4VZ.js +0 -12917
- package/dist/chunk-E3SPN4VZ.js.map +0 -1
- package/dist/chunk-E66EQZE6 5.js +0 -37
- package/dist/chunk-E66EQZE6.js 2.map +0 -1
- package/dist/chunk-HWIIPPNI.js.map +0 -1
- package/dist/chunk-I7PSE6JW 5.js +0 -191
- package/dist/chunk-I7PSE6JW.js 2.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-IIELH4DL.js.map +0 -1
- package/dist/chunk-KNC55RTG.js 5.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js 5.map +0 -1
- package/dist/chunk-LFNCN2SP.js +0 -412
- package/dist/chunk-LFNCN2SP.js 2.map +0 -1
- package/dist/chunk-LFNCN2SP.js.map +0 -1
- package/dist/chunk-LMC26NLJ 2.js +0 -84
- package/dist/chunk-NOAYCWCX.js +0 -4993
- package/dist/chunk-NOAYCWCX.js.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js.map +0 -1
- package/dist/chunk-QXHPKYJV 3.js +0 -113
- package/dist/chunk-R77UEZ4E.js +0 -68
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
- package/dist/chunk-XNXXZ43G.js.map +0 -1
- package/dist/chunk-ZSAAAMVR 6.js +0 -25
- package/dist/components.js 5.map +0 -1
- package/dist/styles/index 2.js +0 -12
- package/dist/styles/index.js 5.map +0 -1
- package/dist/theming/runtime 5.js +0 -19
- package/dist/theming/runtime.js 5.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 -666
- /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-RGJTDE2C.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/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Documentation Check Module
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Audit/Checks/Documentation
|
|
7
|
+
*
|
|
8
|
+
* Checks for:
|
|
9
|
+
* - Missing JSDoc on exported functions/components
|
|
10
|
+
* - Missing README updates
|
|
11
|
+
* - Outdated comments
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { getRelativePath, getLineNumber } = require('../utils.cjs');
|
|
17
|
+
|
|
18
|
+
const documentationCheck = {
|
|
19
|
+
name: 'documentation',
|
|
20
|
+
description: 'Documentation coverage (JSDoc, README)',
|
|
21
|
+
severity: 'suggestion',
|
|
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
|
+
// Detect if this is the pace-core repository itself
|
|
34
|
+
const packagesCorePath = path.join(projectRoot, 'packages', 'core');
|
|
35
|
+
const isPaceCoreRepository = fs.existsSync(packagesCorePath);
|
|
36
|
+
|
|
37
|
+
// Check for README
|
|
38
|
+
const readmePath = path.join(projectRoot, 'README.md');
|
|
39
|
+
if (!fs.existsSync(readmePath)) {
|
|
40
|
+
suggestions.push({
|
|
41
|
+
type: 'missing-readme',
|
|
42
|
+
file: 'README.md',
|
|
43
|
+
message: 'README.md file is missing',
|
|
44
|
+
recommendation: 'Create a README.md with project description, setup instructions, and usage examples'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check for JSDoc on exported functions/components
|
|
49
|
+
for (const filePath of files) {
|
|
50
|
+
try {
|
|
51
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
52
|
+
const relativePath = getRelativePath(filePath, projectRoot);
|
|
53
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
54
|
+
|
|
55
|
+
// Skip test files
|
|
56
|
+
if (normalizedPath.includes('.test.') || normalizedPath.includes('.spec.')) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Skip test directories
|
|
61
|
+
if (normalizedPath.includes('/__tests__/') || normalizedPath.includes('__tests__/')) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Skip scripts directory (build/test scripts, not public API)
|
|
66
|
+
if (normalizedPath.includes('/scripts/') || normalizedPath.startsWith('scripts/')) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Skip generated files
|
|
71
|
+
if (normalizedPath.includes('.generated.') || normalizedPath.includes('.gen.')) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// When running in pace-core repository, skip non-public API files
|
|
76
|
+
if (isPaceCoreRepository) {
|
|
77
|
+
// Skip examples - these are demonstration code, not public API
|
|
78
|
+
if (normalizedPath.includes('packages/core/examples/') ||
|
|
79
|
+
normalizedPath.startsWith('packages/core/examples/')) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Skip root-level src directory - in pace-core repository, this is a demo/showcase app
|
|
84
|
+
if (normalizedPath.startsWith('src/') && !normalizedPath.startsWith('packages/core/src/')) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Skip test setup files
|
|
89
|
+
if (normalizedPath.includes('test-setup.') || normalizedPath.includes('test-setup/')) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Skip database.generated.ts - this is a generated file
|
|
94
|
+
if (normalizedPath.includes('database.generated.ts')) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Find exported functions/components
|
|
100
|
+
const exportPattern = /export\s+(?:default\s+)?(?:async\s+)?(function|const|class|interface|type)\s+(\w+)/g;
|
|
101
|
+
let match;
|
|
102
|
+
while ((match = exportPattern.exec(content)) !== null) {
|
|
103
|
+
const exportName = match[2];
|
|
104
|
+
const exportType = match[1];
|
|
105
|
+
const exportIndex = match.index;
|
|
106
|
+
|
|
107
|
+
// Check if there's JSDoc before this export
|
|
108
|
+
const beforeExport = content.substring(Math.max(0, exportIndex - 500), exportIndex);
|
|
109
|
+
const hasJSDoc = /\/\*\*[\s\S]*?\*\//.test(beforeExport) ||
|
|
110
|
+
/\/\/\/\s*</.test(beforeExport);
|
|
111
|
+
|
|
112
|
+
// Only check public exports (not internal utilities)
|
|
113
|
+
const isPublic = match[0].includes('export') &&
|
|
114
|
+
!normalizedPath.includes('/internal/') &&
|
|
115
|
+
!normalizedPath.includes('/private/');
|
|
116
|
+
|
|
117
|
+
if (!isPublic || hasJSDoc) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Get the content after the export to analyze what it is
|
|
122
|
+
const afterExport = content.substring(exportIndex, Math.min(content.length, exportIndex + 200));
|
|
123
|
+
|
|
124
|
+
// Skip simple type aliases (e.g., `type UUID = string`, `type X = 'a' | 'b'`)
|
|
125
|
+
if (exportType === 'type') {
|
|
126
|
+
// Check if it's a simple type alias (not a complex type)
|
|
127
|
+
const simpleTypePattern = /type\s+\w+\s*=\s*(?:string|number|boolean|'[^']+'|"[^"]+"|`[^`]+`|[\w.]+(?:\s*\|\s*[\w.]+)*)\s*[;=]/;
|
|
128
|
+
if (simpleTypePattern.test(afterExport)) {
|
|
129
|
+
continue; // Skip simple type aliases - they're self-documenting
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Skip Props interfaces that have inline JSDoc on properties
|
|
134
|
+
if (exportType === 'interface' && exportName.endsWith('Props')) {
|
|
135
|
+
// Check if the interface has inline JSDoc comments on its properties
|
|
136
|
+
const interfaceContent = content.substring(exportIndex, Math.min(content.length, exportIndex + 2000));
|
|
137
|
+
const hasInlineJSDoc = /\/\*\*[\s\S]*?\*\//.test(interfaceContent) ||
|
|
138
|
+
/\/\/\/\s*</.test(interfaceContent);
|
|
139
|
+
// If it has inline JSDoc on properties, the interface itself doesn't need a block comment
|
|
140
|
+
if (hasInlineJSDoc) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Skip simple Props type aliases that just extend React types
|
|
146
|
+
if (exportType === 'type' && exportName.endsWith('Props')) {
|
|
147
|
+
// Check if it's a simple type alias extending React types
|
|
148
|
+
const simplePropsPattern = /type\s+\w+Props\s*=\s*React\.\w+Attributes/;
|
|
149
|
+
if (simplePropsPattern.test(afterExport)) {
|
|
150
|
+
continue; // Skip simple Props type aliases - they're self-documenting
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Skip simple constants that are self-explanatory
|
|
155
|
+
if (exportType === 'const') {
|
|
156
|
+
// Check if it's a simple constant assignment (not a function/component)
|
|
157
|
+
const simpleConstPattern = /const\s+\w+\s*=\s*(?:['"`][^'"`]+['"`]|\d+|true|false|null|undefined|\[\]|\{\})\s*[;=]/;
|
|
158
|
+
if (simpleConstPattern.test(afterExport)) {
|
|
159
|
+
// Skip very simple constants - they're self-documenting
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// All other exports should have JSDoc
|
|
165
|
+
suggestions.push({
|
|
166
|
+
type: 'missing-jsdoc',
|
|
167
|
+
file: relativePath,
|
|
168
|
+
line: getLineNumber(content, exportIndex),
|
|
169
|
+
message: `Exported ${exportType} '${exportName}' missing JSDoc documentation`,
|
|
170
|
+
recommendation: 'Add JSDoc comments to document the function/component purpose, parameters, and return value'
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Check for TODO/FIXME comments that might be outdated
|
|
175
|
+
const todoPattern = /(?:TODO|FIXME|XXX|HACK|NOTE):\s*(.+)/gi;
|
|
176
|
+
let todoMatch;
|
|
177
|
+
const todos = [];
|
|
178
|
+
while ((todoMatch = todoPattern.exec(content)) !== null) {
|
|
179
|
+
todos.push({
|
|
180
|
+
text: todoMatch[1],
|
|
181
|
+
line: getLineNumber(content, todoMatch.index)
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (todos.length > 5) {
|
|
186
|
+
suggestions.push({
|
|
187
|
+
type: 'many-todos',
|
|
188
|
+
file: relativePath,
|
|
189
|
+
message: `File has ${todos.length} TODO/FIXME comments`,
|
|
190
|
+
recommendation: 'Review and address TODO comments, or remove outdated ones'
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
} catch (error) {
|
|
195
|
+
// Skip files with errors
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return { issues, warnings, suggestions };
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
module.exports = documentationCheck;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Environment Variables Check Module
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Audit/Checks/Environment
|
|
7
|
+
*
|
|
8
|
+
* Checks for:
|
|
9
|
+
* - Missing required environment variables
|
|
10
|
+
* - Hardcoded secrets/API keys
|
|
11
|
+
* - Incorrect environment variable usage
|
|
12
|
+
* - Missing .env.example file
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
const environmentCheck = {
|
|
19
|
+
name: 'environment',
|
|
20
|
+
description: 'Environment variable usage (secrets, required vars, .env.example)',
|
|
21
|
+
severity: 'warning',
|
|
22
|
+
|
|
23
|
+
async run(context) {
|
|
24
|
+
const { projectRoot, files } = context;
|
|
25
|
+
const issues = [];
|
|
26
|
+
const warnings = [];
|
|
27
|
+
const suggestions = [];
|
|
28
|
+
|
|
29
|
+
// Check for .env.example
|
|
30
|
+
const envExamplePath = path.join(projectRoot, '.env.example');
|
|
31
|
+
if (!fs.existsSync(envExamplePath)) {
|
|
32
|
+
suggestions.push({
|
|
33
|
+
type: 'missing-env-example',
|
|
34
|
+
file: '.env.example',
|
|
35
|
+
message: '.env.example file is missing',
|
|
36
|
+
recommendation: 'Create .env.example with all required environment variables (without actual values)'
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Required environment variables for pace-core apps
|
|
41
|
+
const requiredEnvVars = [
|
|
42
|
+
'VITE_SUPABASE_URL',
|
|
43
|
+
'VITE_SUPABASE_PUBLISHABLE_KEY'
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
// Check .env.example for required vars
|
|
47
|
+
if (fs.existsSync(envExamplePath)) {
|
|
48
|
+
try {
|
|
49
|
+
const envExampleContent = fs.readFileSync(envExamplePath, 'utf8');
|
|
50
|
+
requiredEnvVars.forEach(envVar => {
|
|
51
|
+
if (!envExampleContent.includes(envVar)) {
|
|
52
|
+
warnings.push({
|
|
53
|
+
type: 'missing-env-var',
|
|
54
|
+
file: '.env.example',
|
|
55
|
+
message: `Required environment variable '${envVar}' missing from .env.example`,
|
|
56
|
+
recommendation: `Add ${envVar} to .env.example file`
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
} catch (error) {
|
|
61
|
+
// Skip if can't read
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Check source files for hardcoded secrets
|
|
66
|
+
if (files && files.length > 0) {
|
|
67
|
+
const secretPatterns = [
|
|
68
|
+
/(?:api[_-]?key|secret|password|token)\s*[:=]\s*['"]([^'"]{10,})['"]/gi,
|
|
69
|
+
/(?:supabase[_-]?url|supabase[_-]?key)\s*[:=]\s*['"]https?:\/\/[^'"]+['"]/gi
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
for (const filePath of files) {
|
|
73
|
+
try {
|
|
74
|
+
// Skip test files and config files (they may have example values)
|
|
75
|
+
const relativePath = getRelativePath(filePath, projectRoot);
|
|
76
|
+
if (relativePath.includes('.test.') ||
|
|
77
|
+
relativePath.includes('.spec.') ||
|
|
78
|
+
relativePath.includes('.example.') ||
|
|
79
|
+
relativePath.includes('config')) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
84
|
+
|
|
85
|
+
secretPatterns.forEach(pattern => {
|
|
86
|
+
let match;
|
|
87
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
88
|
+
// Check if it's in a comment
|
|
89
|
+
const beforeMatch = content.substring(Math.max(0, match.index - 100), match.index);
|
|
90
|
+
const isInComment = beforeMatch.includes('//') || beforeMatch.includes('/*');
|
|
91
|
+
|
|
92
|
+
if (!isInComment) {
|
|
93
|
+
issues.push({
|
|
94
|
+
type: 'hardcoded-secret',
|
|
95
|
+
file: relativePath,
|
|
96
|
+
line: content.substring(0, match.index).split('\n').length,
|
|
97
|
+
message: 'Potential hardcoded secret or API key detected',
|
|
98
|
+
recommendation: 'Move secrets to environment variables and use import.meta.env or process.env'
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Check for direct Supabase URL/key usage (should use env vars)
|
|
105
|
+
if (content.includes('supabase') &&
|
|
106
|
+
(content.includes('https://') || content.includes('eyJ'))) {
|
|
107
|
+
const hasEnvVar = /import\.meta\.env|process\.env/.test(content);
|
|
108
|
+
if (!hasEnvVar) {
|
|
109
|
+
warnings.push({
|
|
110
|
+
type: 'hardcoded-config',
|
|
111
|
+
file: relativePath,
|
|
112
|
+
message: 'Supabase configuration may be hardcoded',
|
|
113
|
+
recommendation: 'Use environment variables: import.meta.env.VITE_SUPABASE_URL'
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
} catch (error) {
|
|
119
|
+
// Skip files with errors
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { issues, warnings, suggestions };
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
module.exports = environmentCheck;
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error Handling Check Module
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Audit/Checks/ErrorHandling
|
|
7
|
+
*
|
|
8
|
+
* Checks for:
|
|
9
|
+
* - Missing error boundaries
|
|
10
|
+
* - Unhandled promise rejections
|
|
11
|
+
* - Missing error handling in async functions
|
|
12
|
+
* - Silent error swallowing
|
|
13
|
+
* - Missing error states in components
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const { getRelativePath, getLineNumber } = require('../utils.cjs');
|
|
19
|
+
|
|
20
|
+
const errorHandlingCheck = {
|
|
21
|
+
name: 'error-handling',
|
|
22
|
+
description: 'Error handling patterns (error boundaries, async error handling)',
|
|
23
|
+
severity: 'warning',
|
|
24
|
+
|
|
25
|
+
async run(context) {
|
|
26
|
+
const { projectRoot, files } = context;
|
|
27
|
+
const issues = [];
|
|
28
|
+
const warnings = [];
|
|
29
|
+
const suggestions = [];
|
|
30
|
+
|
|
31
|
+
if (!files || files.length === 0) {
|
|
32
|
+
return { issues, warnings, suggestions };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Skip if this is the pace-core repository itself
|
|
36
|
+
// Detect pace-core repository by checking if packages/core exists
|
|
37
|
+
const packagesCorePath = path.join(projectRoot, 'packages', 'core');
|
|
38
|
+
const isPaceCoreRepository = fs.existsSync(packagesCorePath);
|
|
39
|
+
|
|
40
|
+
// Check for ErrorBoundary usage
|
|
41
|
+
let hasErrorBoundary = false;
|
|
42
|
+
const mainFiles = ['main.tsx', 'main.ts', 'App.tsx', 'App.ts', 'index.tsx', 'index.ts'];
|
|
43
|
+
|
|
44
|
+
for (const filePath of files) {
|
|
45
|
+
try {
|
|
46
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
47
|
+
const relativePath = getRelativePath(filePath, projectRoot);
|
|
48
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
49
|
+
const fileName = path.basename(relativePath);
|
|
50
|
+
|
|
51
|
+
// Skip root-level src directory - in pace-core repository, this is a demo/showcase app
|
|
52
|
+
// Note: We DO check packages/core/ files because error handling issues (missing try/catch, missing .catch()) are real issues that should be fixed
|
|
53
|
+
const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
|
|
54
|
+
if (isRootSrc) {
|
|
55
|
+
continue; // Skip demo app files
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Skip scripts directory - utility scripts don't need error handling validation
|
|
59
|
+
const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
|
|
60
|
+
if (isScript) {
|
|
61
|
+
continue; // Skip script files
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check for ErrorBoundary
|
|
65
|
+
if (content.includes('ErrorBoundary') || content.includes('error-boundary')) {
|
|
66
|
+
hasErrorBoundary = true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check for unhandled promise rejections
|
|
70
|
+
const asyncFunctionPattern = /(async\s+function|const\s+\w+\s*=\s*async|export\s+async\s+function)/g;
|
|
71
|
+
let asyncMatch;
|
|
72
|
+
while ((asyncMatch = asyncFunctionPattern.exec(content)) !== null) {
|
|
73
|
+
const functionStart = asyncMatch.index;
|
|
74
|
+
const beforeAsync = content.substring(Math.max(0, functionStart - 100), functionStart);
|
|
75
|
+
const afterAsync = content.substring(functionStart, Math.min(content.length, functionStart + 1000));
|
|
76
|
+
|
|
77
|
+
// Check if function has try/catch
|
|
78
|
+
const hasTryCatch = /try\s*\{/.test(afterAsync);
|
|
79
|
+
|
|
80
|
+
// Check for await calls
|
|
81
|
+
const hasAwait = /await\s+/.test(afterAsync);
|
|
82
|
+
|
|
83
|
+
// Check if this is an exported library function (designed to throw errors for callers to handle)
|
|
84
|
+
// Look for "export" before the async function declaration
|
|
85
|
+
const isExportedLibraryFunction = /export\s+(async\s+function|function)/.test(beforeAsync + asyncMatch[0]) ||
|
|
86
|
+
asyncMatch[0].includes('export');
|
|
87
|
+
|
|
88
|
+
// Skip pace-core files - library code has different error handling patterns
|
|
89
|
+
// Exported functions are designed to throw errors for callers to handle
|
|
90
|
+
// Internal utility functions may also intentionally propagate errors
|
|
91
|
+
const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
|
|
92
|
+
|
|
93
|
+
if (hasAwait && !hasTryCatch && !isExportedLibraryFunction && !isPaceCorePackage) {
|
|
94
|
+
warnings.push({
|
|
95
|
+
type: 'unhandled-async',
|
|
96
|
+
file: relativePath,
|
|
97
|
+
line: getLineNumber(content, asyncMatch.index),
|
|
98
|
+
message: 'Async function with await calls but no try/catch error handling',
|
|
99
|
+
recommendation: 'Wrap await calls in try/catch blocks to handle errors gracefully'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check for .then() without .catch()
|
|
105
|
+
// Look for .then() and check if there's a .catch() later in the chain (not just immediately after)
|
|
106
|
+
const thenPattern = /\.then\s*\([^)]*\)/g;
|
|
107
|
+
let thenMatch;
|
|
108
|
+
while ((thenMatch = thenPattern.exec(content)) !== null) {
|
|
109
|
+
// Check if there's a .catch() after this .then() in the promise chain
|
|
110
|
+
// Look ahead up to 500 chars (reasonable for a promise chain)
|
|
111
|
+
const afterThen = content.substring(thenMatch.index, Math.min(content.length, thenMatch.index + 500));
|
|
112
|
+
|
|
113
|
+
// Check if there's a .catch() in the chain
|
|
114
|
+
// Also check for .then().catch() pattern
|
|
115
|
+
const hasCatch = /\.catch\s*\(/.test(afterThen);
|
|
116
|
+
|
|
117
|
+
// Check if the .then() handler has try/catch inside it (valid pattern: .then(async () => { try { ... } catch { ... } }))
|
|
118
|
+
const hasTryCatchInHandler = /\.then\s*\(\s*async\s*\([^)]*\)\s*=>\s*\{[^}]*try\s*\{/.test(afterThen);
|
|
119
|
+
|
|
120
|
+
// Also check if the .then() itself handles errors (returns error object)
|
|
121
|
+
const thenBody = thenMatch[0];
|
|
122
|
+
const handlesErrorInThen = /error|Error|catch/.test(thenBody);
|
|
123
|
+
|
|
124
|
+
// Skip pace-core files - library code has complex patterns that are hard to detect accurately
|
|
125
|
+
const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
|
|
126
|
+
|
|
127
|
+
if (!hasCatch && !handlesErrorInThen && !hasTryCatchInHandler && !isPaceCorePackage) {
|
|
128
|
+
warnings.push({
|
|
129
|
+
type: 'unhandled-promise',
|
|
130
|
+
file: relativePath,
|
|
131
|
+
line: getLineNumber(content, thenMatch.index),
|
|
132
|
+
message: 'Promise chain with .then() but no .catch() handler',
|
|
133
|
+
recommendation: 'Add .catch() handler to handle promise rejections. The .catch() can come after the .then() in the chain.'
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check for console.error without proper error handling
|
|
139
|
+
const consoleErrorPattern = /console\.error\s*\([^)]*\)/g;
|
|
140
|
+
let consoleMatch;
|
|
141
|
+
while ((consoleMatch = consoleErrorPattern.exec(content)) !== null) {
|
|
142
|
+
// Skip console.error in comments/JSDoc (example code)
|
|
143
|
+
const beforeMatch = content.substring(Math.max(0, consoleMatch.index - 500), consoleMatch.index);
|
|
144
|
+
const afterMatch = content.substring(consoleMatch.index, Math.min(content.length, consoleMatch.index + 50));
|
|
145
|
+
|
|
146
|
+
// Get the line number to check if we're in a comment block
|
|
147
|
+
const errorLineNum = getLineNumber(content, consoleMatch.index);
|
|
148
|
+
const lines = content.split('\n');
|
|
149
|
+
const errorLine = lines[errorLineNum - 1]; // Line numbers are 1-indexed
|
|
150
|
+
|
|
151
|
+
// Check if the line starts with * (JSDoc comment block) or // (single line comment)
|
|
152
|
+
const isInCommentLine = /^\s*\*|^\s*\/\//.test(errorLine);
|
|
153
|
+
|
|
154
|
+
// Check if we're inside a /* ... */ or /** ... */ block
|
|
155
|
+
// Find the last /* or /** before this position, and check if */ comes after
|
|
156
|
+
const lastCommentStart = beforeMatch.lastIndexOf('/*');
|
|
157
|
+
const lastCommentEndBefore = beforeMatch.lastIndexOf('*/');
|
|
158
|
+
const firstCommentEndAfter = afterMatch.indexOf('*/');
|
|
159
|
+
// We're in a comment block if there's a /* before and no */ before it (or */ is after)
|
|
160
|
+
const isInCommentBlock = lastCommentStart !== -1 &&
|
|
161
|
+
(lastCommentStart > lastCommentEndBefore || firstCommentEndAfter !== -1);
|
|
162
|
+
|
|
163
|
+
// Check if it's in a comment (JSDoc example or comment block)
|
|
164
|
+
if (isInCommentLine || isInCommentBlock) {
|
|
165
|
+
continue; // Skip JSDoc examples and comment blocks
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Skip example files - these are demonstration code, not production code
|
|
169
|
+
const isExampleFile = normalizedPath.includes('/examples/') || normalizedPath.includes('Example.tsx') || normalizedPath.includes('Example.ts');
|
|
170
|
+
if (isExampleFile) {
|
|
171
|
+
continue; // Skip example files
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Skip logger utility files - these ARE the error handling mechanism
|
|
175
|
+
const isLoggerUtility = normalizedPath.includes('logger') ||
|
|
176
|
+
normalizedPath.includes('debugLogger') ||
|
|
177
|
+
normalizedPath.includes('rbac/config.ts'); // RBAC config is a logger setup
|
|
178
|
+
if (isLoggerUtility) {
|
|
179
|
+
continue; // Skip logger utilities
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Skip ESLint rule files - these are tooling, not application code
|
|
183
|
+
const isEslintRule = normalizedPath.includes('eslint-rules') || normalizedPath.includes('eslint.config');
|
|
184
|
+
if (isEslintRule) {
|
|
185
|
+
continue; // Skip ESLint rules
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Skip performance monitoring utilities - these are informational logging
|
|
189
|
+
const isPerformanceUtility = normalizedPath.includes('performance') &&
|
|
190
|
+
(normalizedPath.includes('performanceBudgets') || normalizedPath.includes('performanceBenchmark'));
|
|
191
|
+
if (isPerformanceUtility) {
|
|
192
|
+
continue; // Skip performance monitoring
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Check if it's just logging without handling
|
|
196
|
+
// Also check after the console.error to see if error state is set or error is returned
|
|
197
|
+
const afterError = content.substring(consoleMatch.index, Math.min(content.length, consoleMatch.index + 200));
|
|
198
|
+
const hasErrorHandling = beforeMatch.includes('throw') ||
|
|
199
|
+
beforeMatch.includes('return') ||
|
|
200
|
+
beforeMatch.includes('catch') ||
|
|
201
|
+
afterError.includes('throw') || // Check after as well (e.g., console.error then throw)
|
|
202
|
+
afterError.includes('setError') ||
|
|
203
|
+
afterError.includes('error =') ||
|
|
204
|
+
afterError.includes('onError') ||
|
|
205
|
+
afterError.includes('onUploadError');
|
|
206
|
+
|
|
207
|
+
if (!hasErrorHandling) {
|
|
208
|
+
suggestions.push({
|
|
209
|
+
type: 'error-logging-only',
|
|
210
|
+
file: relativePath,
|
|
211
|
+
line: getLineNumber(content, consoleMatch.index),
|
|
212
|
+
message: 'Error is logged but may not be handled properly',
|
|
213
|
+
recommendation: 'Ensure errors are properly handled (thrown, returned, or displayed to users)'
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Check for missing error states in components using pace-core hooks
|
|
219
|
+
// Only check React component files
|
|
220
|
+
if (filePath.match(/\.(tsx|jsx)$/)) {
|
|
221
|
+
const paceCoreHooks = ['useUnifiedAuth', 'useOrganisations', 'useEvents', 'useSecureSupabase'];
|
|
222
|
+
paceCoreHooks.forEach(hookName => {
|
|
223
|
+
if (content.includes(hookName)) {
|
|
224
|
+
const hookPattern = new RegExp(`const\\s+[^=]+=\\s+${hookName}\\s*\\(`, 'g');
|
|
225
|
+
let hookMatch;
|
|
226
|
+
while ((hookMatch = hookPattern.exec(content)) !== null) {
|
|
227
|
+
// Check the entire component, not just 500 chars after hook
|
|
228
|
+
// React's Rules of Hooks require all hooks to be called before conditional returns,
|
|
229
|
+
// so error handling may occur later in the component
|
|
230
|
+
const hookLine = getLineNumber(content, hookMatch.index);
|
|
231
|
+
const lines = content.split('\n');
|
|
232
|
+
const hookLineIndex = hookLine - 1;
|
|
233
|
+
|
|
234
|
+
// Get the rest of the component after this hook
|
|
235
|
+
const afterHookContent = lines.slice(hookLineIndex).join('\n');
|
|
236
|
+
|
|
237
|
+
// Check if error is destructured from the hook
|
|
238
|
+
const hookCallLine = lines[hookLineIndex];
|
|
239
|
+
const hasErrorDestructured = /(error|Error|authError|orgError)/.test(hookCallLine);
|
|
240
|
+
|
|
241
|
+
// For useSecureSupabase, check for null checks (it returns null on error)
|
|
242
|
+
const isSecureSupabase = hookName === 'useSecureSupabase';
|
|
243
|
+
const hasNullCheck = isSecureSupabase && /secureSupabase\s*===?\s*null|!secureSupabase/.test(afterHookContent);
|
|
244
|
+
|
|
245
|
+
// Check for error handling patterns in the entire component:
|
|
246
|
+
// 1. Component-level error checks with early returns: if (error) return <ErrorUI />
|
|
247
|
+
// 2. useEffect error handlers: useEffect(() => { if (error) ... }, [error])
|
|
248
|
+
// 3. Null checks for useSecureSupabase: if (secureSupabase === null) ...
|
|
249
|
+
// 4. Error passed to error handling utilities
|
|
250
|
+
|
|
251
|
+
const hasComponentLevelErrorCheck = /if\s*\(\s*(error|authError|orgError|rbacError)/.test(afterHookContent);
|
|
252
|
+
const hasUseEffectErrorHandler = /useEffect\s*\([^)]*\([^)]*\)\s*=>\s*\{[^}]*\b(error|authError|orgError|rbacError)\b[^}]*\}/.test(afterHookContent);
|
|
253
|
+
const hasErrorInDeps = /useEffect\s*\([^)]*,\s*\[[^\]]*\b(error|authError|orgError|rbacError)\b/.test(afterHookContent);
|
|
254
|
+
const hasErrorHandling = /if\s*\(\s*(error|authError|orgError|rbacError)|(error|authError|orgError|rbacError)\s*\?|catch/.test(afterHookContent);
|
|
255
|
+
const hasErrorUtility = /handleSupabaseError|handleError|toast.*error|showError/.test(afterHookContent);
|
|
256
|
+
|
|
257
|
+
// Only flag if error is destructured but not handled anywhere
|
|
258
|
+
if (hasErrorDestructured && !hasComponentLevelErrorCheck && !hasUseEffectErrorHandler &&
|
|
259
|
+
!hasErrorInDeps && !hasErrorHandling && !hasErrorUtility && !hasNullCheck) {
|
|
260
|
+
warnings.push({
|
|
261
|
+
type: 'unhandled-hook-error',
|
|
262
|
+
file: relativePath,
|
|
263
|
+
line: hookLine,
|
|
264
|
+
message: `${hookName} may return an error that is not handled`,
|
|
265
|
+
recommendation: 'Check for error state and handle it appropriately. You can handle errors via: (1) Component-level checks after all hooks: if (error) return <ErrorUI />, (2) useEffect hooks that monitor error states, or (3) For useSecureSupabase, null checks: if (secureSupabase === null) ...'
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
} catch (error) {
|
|
273
|
+
// Skip files with errors
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Check if ErrorBoundary is used in main app file
|
|
278
|
+
// Skip this check if this is the pace-core repository (root src/ is a demo app)
|
|
279
|
+
if (!hasErrorBoundary && !isPaceCoreRepository) {
|
|
280
|
+
const mainFile = mainFiles.find(file => {
|
|
281
|
+
const filePath = path.join(projectRoot, 'src', file);
|
|
282
|
+
return fs.existsSync(filePath);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (mainFile) {
|
|
286
|
+
suggestions.push({
|
|
287
|
+
type: 'missing-error-boundary',
|
|
288
|
+
file: `src/${mainFile}`,
|
|
289
|
+
message: 'No ErrorBoundary found in main app file',
|
|
290
|
+
recommendation: 'Wrap your app with ErrorBoundary from @jmruthers/pace-core to catch and handle React errors gracefully'
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return { issues, warnings, suggestions };
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
module.exports = errorHandlingCheck;
|