@jmruthers/pace-core 0.6.5 → 0.6.7
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 +104 -0
- package/README.md +5 -403
- package/audit-tool/00-dependencies.cjs +394 -0
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +255 -0
- package/audit-tool/audits/03-architecture.cjs +196 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +544 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +291 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +241 -0
- package/core-usage-manifest.json +93 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/01-pace-core-compliance.mdc +586 -0
- package/cursor-rules/02-project-structure.mdc +42 -4
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
- package/cursor-rules/06-security-rbac.mdc +518 -0
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/DataTable-7PMH7XN7.js +15 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
- package/dist/chunk-6F3IILHI.js +62 -0
- package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
- package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
- package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
- package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
- package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/chunk-GHYHJTYV.js +994 -0
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
- package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
- package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
- package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
- package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
- package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
- package/dist/components.d.ts +7 -5
- package/dist/components.js +46 -257
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +35 -0
- package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
- package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/eslint-rules/utils/helpers.cjs +42 -0
- package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +62 -172
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +12 -11
- package/dist/index.js +67 -660
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +8 -35
- package/dist/rbac/eslint-rules.d.ts +46 -44
- package/dist/rbac/eslint-rules.js +7 -4
- package/dist/rbac/index.d.ts +109 -586
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- package/dist/theming/runtime.d.ts +14 -1
- package/dist/theming/runtime.js +3 -19
- package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
- package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +17 -7
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +301 -871
- package/docs/api-reference/components.md +21 -21
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +126 -33
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +5 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +17 -8
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -35
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/1-pace-core-compliance-standards.md +986 -0
- package/docs/standards/2-project-structure-standards.md +949 -0
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/5-styling-standards.md +348 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +185 -57
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +33 -6
- package/package.json +35 -23
- package/scripts/install-cursor-rules.cjs +25 -6
- package/scripts/install-eslint-config.cjs +284 -0
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/ContextSelector/ContextSelector.tsx +106 -119
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +12 -9
- package/src/components/DataTable/components/EmptyState.tsx +10 -9
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
- package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
- package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
- package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
- package/src/components/DataTable/types.ts +5 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +10 -9
- package/src/components/Dialog/Dialog.test.tsx +128 -104
- package/src/components/Dialog/Dialog.tsx +742 -24
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
- package/src/components/FileDisplay/FileDisplay.tsx +23 -17
- package/src/components/FileUpload/FileUpload.test.tsx +52 -14
- package/src/components/FileUpload/FileUpload.tsx +112 -130
- package/src/components/Form/Form.test.tsx +6 -8
- package/src/components/Form/Form.tsx +365 -4
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +109 -98
- package/src/components/Select/types.ts +4 -1
- package/src/components/UserMenu/UserMenu.tsx +9 -6
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
- package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
- package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
- package/src/hooks/public/usePublicEvent.ts +67 -195
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +24 -14
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +5 -5
- package/src/hooks/useAppConfig.ts +28 -26
- package/src/hooks/useEventTheme.test.ts +217 -239
- package/src/hooks/useEventTheme.ts +16 -28
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/hooks/useOrganisationPermissions.ts +5 -7
- package/src/hooks/useQueryCache.ts +0 -1
- package/src/hooks/useSessionDraft.ts +380 -0
- package/src/hooks/useSessionRestoration.ts +3 -1
- package/src/icons/index.ts +27 -0
- package/src/index.ts +5 -0
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
- package/src/rbac/adapters.tsx +7 -295
- package/src/rbac/api.test.ts +44 -56
- package/src/rbac/api.ts +10 -17
- package/src/rbac/cache-invalidation.ts +0 -1
- package/src/rbac/compliance/index.ts +10 -0
- package/src/rbac/compliance/pattern-detector.ts +553 -0
- package/src/rbac/compliance/runtime-compliance.ts +22 -0
- package/src/rbac/components/AccessDenied.tsx +150 -0
- package/src/rbac/components/NavigationGuard.tsx +12 -20
- package/src/rbac/components/PagePermissionGuard.tsx +4 -24
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
- package/src/rbac/components/index.ts +3 -41
- package/src/rbac/eslint-rules.js +1 -1
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/permissions/index.ts +0 -3
- package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
- package/src/rbac/hooks/usePermissions.ts +0 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
- package/src/rbac/hooks/useResolvedScope.ts +58 -140
- package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
- package/src/rbac/hooks/useResourcePermissions.ts +139 -48
- package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
- package/src/rbac/hooks/useRoleManagement.ts +147 -19
- package/src/rbac/hooks/useSecureSupabase.ts +4 -8
- package/src/rbac/index.ts +7 -9
- package/src/rbac/utils/contextValidator.ts +9 -7
- package/src/services/AuthService.ts +130 -18
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +16 -0
- package/src/services/OrganisationService.ts +7 -44
- package/src/services/__tests__/OrganisationService.test.ts +26 -8
- package/src/services/base/BaseService.ts +0 -3
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +9 -3
- package/src/theming/parseEventColours.ts +22 -10
- package/src/types/database.generated.ts +4733 -3809
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
- package/src/utils/context/organisationContext.test.ts +13 -28
- package/src/utils/context/organisationContext.ts +21 -52
- package/src/utils/dynamic/dynamicUtils.ts +1 -1
- package/src/utils/file-reference/index.ts +39 -15
- package/src/utils/formatting/formatDateTime.test.ts +3 -2
- package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
- package/src/utils/index.ts +4 -1
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
- package/src/utils/persistence/keyDerivation.ts +304 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
- package/src/utils/security/secureStorage.ts +5 -5
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/helpers.ts +3 -3
- package/src/utils/supabase/createBaseClient.ts +147 -0
- package/src/utils/timezone/timezone.test.ts +1 -2
- package/src/utils/timezone/timezone.ts +1 -1
- package/src/utils/validation/csrf.ts +4 -4
- package/cursor-rules/00-pace-core-compliance.mdc +0 -331
- package/cursor-rules/01-standards-compliance.mdc +0 -244
- package/cursor-rules/04-testing-standards.mdc +0 -268
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
- package/cursor-rules/06-code-quality.mdc +0 -309
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
- package/cursor-rules/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-AOVNCPTX.js +0 -175
- package/dist/DataTable-AOVNCPTX.js.map +0 -1
- package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
- package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
- package/dist/api-O6HTBX5Y.js +0 -52
- package/dist/api-O6HTBX5Y.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6COVEUS7.js.map +0 -1
- package/dist/chunk-AFVQODI2.js +0 -263
- package/dist/chunk-AFVQODI2.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.js.map +0 -1
- package/dist/chunk-EFN2EIMK.js.map +0 -1
- package/dist/chunk-FFQEQTNW.js.map +0 -1
- package/dist/chunk-FMUCXFII.js.map +0 -1
- package/dist/chunk-G37KK66H.js.map +0 -1
- package/dist/chunk-G7QEZTYQ.js +0 -2053
- package/dist/chunk-G7QEZTYQ.js.map +0 -1
- package/dist/chunk-HU2C6SSC.js.map +0 -1
- package/dist/chunk-IHB5DR3H.js.map +0 -1
- package/dist/chunk-IVOFDYWT.js.map +0 -1
- package/dist/chunk-J36DSWQK.js.map +0 -1
- package/dist/chunk-JGRYX5UX.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js +0 -1
- package/dist/chunk-KQCRWDSA.js.map +0 -1
- package/dist/chunk-L4OXEN46.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-M43Y4SSO.js.map +0 -1
- package/dist/chunk-M7MPQISP.js.map +0 -1
- package/dist/chunk-NTM7ZSB6.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-RGAWHO7N.js.map +0 -1
- package/dist/chunk-UPPMRMYG.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-5OGXSPKS.js +0 -9
- package/dist/contextValidator-5OGXSPKS.js.map +0 -1
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
- package/dist/hooks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers.js.map +0 -1
- package/dist/rbac/eslint-rules.js.map +0 -1
- package/dist/rbac/index.js.map +0 -1
- package/dist/styles/index.js.map +0 -1
- package/dist/theming/runtime.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -601
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-architecture-standard.md +0 -44
- package/docs/standards/02-api-and-rpc-standard.md +0 -39
- package/docs/standards/03-component-standard.md +0 -32
- package/docs/standards/04-code-style-standard.md +0 -32
- package/docs/standards/05-security-standard.md +0 -44
- package/docs/standards/06-testing-and-docs-standard.md +0 -29
- package/docs/standards/pace-core-compliance.md +0 -432
- package/scripts/audit/core/checks/accessibility.cjs +0 -197
- package/scripts/audit/core/checks/api-usage.cjs +0 -191
- package/scripts/audit/core/checks/bundle.cjs +0 -142
- package/scripts/audit/core/checks/compliance.cjs +0 -2706
- package/scripts/audit/core/checks/config.cjs +0 -54
- package/scripts/audit/core/checks/coverage.cjs +0 -84
- package/scripts/audit/core/checks/dependencies.cjs +0 -994
- package/scripts/audit/core/checks/documentation.cjs +0 -268
- package/scripts/audit/core/checks/environment.cjs +0 -116
- package/scripts/audit/core/checks/error-handling.cjs +0 -340
- package/scripts/audit/core/checks/forms.cjs +0 -172
- package/scripts/audit/core/checks/heuristics.cjs +0 -68
- package/scripts/audit/core/checks/hooks.cjs +0 -334
- package/scripts/audit/core/checks/imports.cjs +0 -244
- package/scripts/audit/core/checks/performance.cjs +0 -325
- package/scripts/audit/core/checks/routes.cjs +0 -117
- package/scripts/audit/core/checks/state.cjs +0 -130
- package/scripts/audit/core/checks/structure.cjs +0 -65
- package/scripts/audit/core/checks/style.cjs +0 -584
- package/scripts/audit/core/checks/testing.cjs +0 -122
- package/scripts/audit/core/checks/typescript.cjs +0 -61
- package/scripts/audit/core/scanner.cjs +0 -199
- package/scripts/audit/core/utils.cjs +0 -137
- package/scripts/audit/index.cjs +0 -223
- package/scripts/audit/reporters/console.cjs +0 -151
- package/scripts/audit/reporters/json.cjs +0 -54
- package/scripts/audit/reporters/markdown.cjs +0 -124
- package/scripts/audit-consuming-app.cjs +0 -86
- package/src/components/DataTable/components/DataTableBody.tsx +0 -454
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/pace-core-compliance.js +0 -638
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
- package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
- package/src/rbac/components/NavigationProvider.test.tsx +0 -481
- package/src/rbac/components/NavigationProvider.tsx +0 -345
- package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
- package/src/rbac/components/PagePermissionProvider.tsx +0 -279
- package/src/rbac/components/PermissionEnforcer.tsx +0 -312
- package/src/rbac/components/RoleBasedRouter.tsx +0 -440
- package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
- package/src/rbac/components/SecureDataProvider.tsx +0 -339
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Performance Check Module
|
|
5
|
-
* @package @jmruthers/pace-core
|
|
6
|
-
* @module Audit/Checks/Performance
|
|
7
|
-
*
|
|
8
|
-
* Checks for:
|
|
9
|
-
* - Missing React.memo on expensive components
|
|
10
|
-
* - Missing useMemo/useCallback for expensive computations
|
|
11
|
-
* - Unnecessary re-renders
|
|
12
|
-
* - Large inline objects/functions in JSX props
|
|
13
|
-
* - Missing key props in lists
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const fs = require('fs');
|
|
17
|
-
const { getRelativePath, getLineNumber } = require('../utils.cjs');
|
|
18
|
-
|
|
19
|
-
const performanceCheck = {
|
|
20
|
-
name: 'performance',
|
|
21
|
-
description: 'Performance anti-patterns (missing memoization, unnecessary re-renders)',
|
|
22
|
-
severity: 'warning',
|
|
23
|
-
|
|
24
|
-
async run(context) {
|
|
25
|
-
const { projectRoot, files } = context;
|
|
26
|
-
const issues = [];
|
|
27
|
-
const warnings = [];
|
|
28
|
-
const suggestions = [];
|
|
29
|
-
|
|
30
|
-
if (!files || files.length === 0) {
|
|
31
|
-
return { issues, warnings, suggestions };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
for (const filePath of files) {
|
|
35
|
-
try {
|
|
36
|
-
// Only check React component files
|
|
37
|
-
if (!filePath.match(/\.(tsx|jsx)$/)) {
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
42
|
-
const relativePath = getRelativePath(filePath, projectRoot);
|
|
43
|
-
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
44
|
-
|
|
45
|
-
// Skip root-level src directory - in pace-core repository, this is a demo/showcase app
|
|
46
|
-
// Note: We DO check packages/core/ files because performance issues (like missing key props) are real issues that should be fixed
|
|
47
|
-
const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
|
|
48
|
-
if (isRootSrc) {
|
|
49
|
-
continue; // Skip demo app files
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Skip scripts directory - utility scripts don't need performance validation
|
|
53
|
-
const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
|
|
54
|
-
if (isScript) {
|
|
55
|
-
continue; // Skip script files
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Skip examples directory - these are demo/example files, not production code
|
|
59
|
-
// Examples are meant to show usage patterns, not be optimized for performance
|
|
60
|
-
const isExample = normalizedPath.includes('/examples/') || normalizedPath.startsWith('examples/');
|
|
61
|
-
if (isExample) {
|
|
62
|
-
continue; // Skip example files
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Skip test files - test code doesn't need performance optimization
|
|
66
|
-
const isTestFile = normalizedPath.includes('.test.') ||
|
|
67
|
-
normalizedPath.includes('.spec.') ||
|
|
68
|
-
normalizedPath.includes('test-setup') ||
|
|
69
|
-
normalizedPath.includes('__tests__');
|
|
70
|
-
if (isTestFile) {
|
|
71
|
-
continue; // Skip test files
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// For library code (packages/core/src), be more lenient with performance suggestions
|
|
75
|
-
// Library components often use inline functions which are acceptable patterns
|
|
76
|
-
const isLibraryCode = normalizedPath.includes('packages/core/src/');
|
|
77
|
-
|
|
78
|
-
// Check for components that could benefit from React.memo
|
|
79
|
-
// Note: For library components, React.memo should be used judiciously
|
|
80
|
-
// We only suggest it for components that are likely to render frequently
|
|
81
|
-
const componentPattern = /export\s+(default\s+)?(function|const)\s+(\w+)\s*[=\(]/g;
|
|
82
|
-
let match;
|
|
83
|
-
while ((match = componentPattern.exec(content)) !== null) {
|
|
84
|
-
const componentName = match[3];
|
|
85
|
-
const isMemoized = content.includes(`React.memo(${componentName})`) ||
|
|
86
|
-
content.includes(`memo(${componentName})`) ||
|
|
87
|
-
content.includes(`export default memo(${componentName})`);
|
|
88
|
-
|
|
89
|
-
// Check if component receives props and is not memoized
|
|
90
|
-
if (match[0].includes('props') || match[0].includes('{') || content.includes(`${componentName}({`)) {
|
|
91
|
-
// Check if component has significant JSX (heuristic: more than 5 lines of JSX)
|
|
92
|
-
const componentStart = match.index;
|
|
93
|
-
const afterMatch = content.substring(componentStart);
|
|
94
|
-
const jsxLines = (afterMatch.match(/<[A-Z]/g) || []).length;
|
|
95
|
-
|
|
96
|
-
// Skip if component uses forwardRef (memoization handled differently)
|
|
97
|
-
// Skip if component is a hook (starts with 'use')
|
|
98
|
-
// Skip if component is very simple (less than 5 JSX elements)
|
|
99
|
-
// Skip if component is already memoized
|
|
100
|
-
if (jsxLines > 5 && !isMemoized && !content.includes('forwardRef') && !componentName.startsWith('use')) {
|
|
101
|
-
// For library components, be more conservative - only suggest for components
|
|
102
|
-
// that are likely to be in lists or render frequently
|
|
103
|
-
// Skip if it's a page-level component (like *Page, *Layout, *Route)
|
|
104
|
-
const isPageComponent = /Page|Layout|Route|Modal|Dialog/.test(componentName);
|
|
105
|
-
|
|
106
|
-
// Only suggest memo for components that are likely to render frequently
|
|
107
|
-
// (not page-level components, which typically render once per route)
|
|
108
|
-
// For library code, skip React.memo suggestions entirely - memoization decisions
|
|
109
|
-
// should be made by the consuming application based on their specific use case
|
|
110
|
-
if (!isPageComponent && !isLibraryCode) {
|
|
111
|
-
suggestions.push({
|
|
112
|
-
type: 'missing-memo',
|
|
113
|
-
file: relativePath,
|
|
114
|
-
line: getLineNumber(content, match.index),
|
|
115
|
-
message: `Component '${componentName}' receives props but is not memoized`,
|
|
116
|
-
recommendation: `Consider wrapping with React.memo if the component renders frequently: export default React.memo(${componentName})`
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
// For library code, skip React.memo suggestions - library components are often
|
|
120
|
-
// controlled by parent components, and memoization should be decided by consumers
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Check for large inline objects/functions in JSX props
|
|
126
|
-
// But exclude JSX children (which is standard React pattern)
|
|
127
|
-
const jsxPropPattern = /<[A-Z]\w+\s+[^>]*\{[^}]{100,}[^>]*>/g;
|
|
128
|
-
let jsxMatch;
|
|
129
|
-
while ((jsxMatch = jsxPropPattern.exec(content)) !== null) {
|
|
130
|
-
const matchText = jsxMatch[0];
|
|
131
|
-
|
|
132
|
-
// Check if this is JSX children (element prop) or guard component props
|
|
133
|
-
// Patterns to exclude (these are valid React patterns):
|
|
134
|
-
// 1. <Route element={<Component />} /> - React Router pattern
|
|
135
|
-
// 2. <GuardComponent fallback={<Component />} /> - Guard component pattern
|
|
136
|
-
// 3. <Component>{children}</Component> - JSX children (but this won't match our pattern)
|
|
137
|
-
// 4. Any prop that contains JSX elements (starts with <)
|
|
138
|
-
|
|
139
|
-
const isRouteElement = /element\s*=\s*\{/.test(matchText);
|
|
140
|
-
const isGuardProp = /(fallback|loading|error|children)\s*=\s*\{/.test(matchText);
|
|
141
|
-
const hasJSXElement = /<[A-Z]\w+/.test(matchText);
|
|
142
|
-
|
|
143
|
-
// Also check if it's a className utility call (like cn()) - not a performance issue
|
|
144
|
-
const isUtilityCall = /cn\s*\(|clsx\s*\(|classnames\s*\(/.test(matchText);
|
|
145
|
-
|
|
146
|
-
// Only flag if it's an actual inline object/function, not JSX children
|
|
147
|
-
if (!isRouteElement && !isGuardProp && !hasJSXElement && !isUtilityCall) {
|
|
148
|
-
warnings.push({
|
|
149
|
-
type: 'inline-object-in-jsx',
|
|
150
|
-
file: relativePath,
|
|
151
|
-
line: getLineNumber(content, jsxMatch.index),
|
|
152
|
-
message: 'Large inline object or function in JSX props detected',
|
|
153
|
-
recommendation: 'Extract inline objects/functions to variables or useMemo/useCallback to prevent unnecessary re-renders. JSX children in props (like React Router element prop or guard component fallback prop) are standard React patterns and not performance issues.'
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Check for missing key props in lists
|
|
159
|
-
// Only flag if JSX is returned, not data arrays
|
|
160
|
-
const mapPattern = /\.map\s*\(\s*\([^)]+\)\s*=>/g;
|
|
161
|
-
while ((match = mapPattern.exec(content)) !== null) {
|
|
162
|
-
const afterMap = content.substring(match.index, match.index + 500);
|
|
163
|
-
// Check if JSX is returned without key
|
|
164
|
-
// Also check if it's actually returning JSX (not just data objects)
|
|
165
|
-
const returnsJSX = afterMap.includes('return') && afterMap.includes('<');
|
|
166
|
-
|
|
167
|
-
// Check for key in various formats: key=, key:, or inside React.cloneElement
|
|
168
|
-
const hasKey = afterMap.includes('key=') ||
|
|
169
|
-
/key\s*:/.test(afterMap) ||
|
|
170
|
-
/React\.cloneElement\s*\([^,]+,\s*\{[^}]*key\s*:/.test(afterMap);
|
|
171
|
-
|
|
172
|
-
// Check if it's a data transformation (returns object/array/primitive, not JSX)
|
|
173
|
-
// Pattern 1: Returns a simple value like .map(([key, _]) => key)
|
|
174
|
-
const returnsSimpleValue = /=>\s*[a-zA-Z_$][a-zA-Z0-9_$]*\s*;/.test(afterMap) && !afterMap.includes('<');
|
|
175
|
-
// Pattern 2: Returns a data object without JSX
|
|
176
|
-
const returnsDataObject = /return\s*\{[^<]*\}/.test(afterMap) && !afterMap.match(/return\s*\{[^<]*<[A-Z]/);
|
|
177
|
-
// Pattern 3: Returns array element access like .map((subRow) => subRow.original)
|
|
178
|
-
const returnsDataProperty = /=>\s*[a-zA-Z_$][a-zA-Z0-9_$]*\.[a-zA-Z_$][a-zA-Z0-9_$]*\s*[;}]/.test(afterMap) && !afterMap.includes('<');
|
|
179
|
-
|
|
180
|
-
// Skip pace-core files for this check - library code has complex patterns that are hard to detect accurately
|
|
181
|
-
const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
|
|
182
|
-
|
|
183
|
-
if (returnsJSX && !hasKey && !returnsDataObject && !returnsSimpleValue && !returnsDataProperty && !isPaceCorePackage) {
|
|
184
|
-
warnings.push({
|
|
185
|
-
type: 'missing-key',
|
|
186
|
-
file: relativePath,
|
|
187
|
-
line: getLineNumber(content, match.index),
|
|
188
|
-
message: 'Array map returns JSX without key prop',
|
|
189
|
-
recommendation: 'Add a unique key prop to list items: {items.map(item => <Item key={item.id} ... />)}. Data array transformations (not JSX) do not need keys.'
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Check for expensive computations that could use useMemo
|
|
195
|
-
const expensivePatterns = [
|
|
196
|
-
/\.filter\([^)]*\)\.map\(/g, // filter().map() chains
|
|
197
|
-
/\.sort\([^)]*\)\.map\(/g, // sort().map() chains
|
|
198
|
-
/\.reduce\([^)]*\)/g // reduce operations
|
|
199
|
-
];
|
|
200
|
-
|
|
201
|
-
expensivePatterns.forEach(pattern => {
|
|
202
|
-
let expMatch;
|
|
203
|
-
while ((expMatch = pattern.exec(content)) !== null) {
|
|
204
|
-
// Check a larger context to see if it's already in useMemo or useCallback
|
|
205
|
-
const beforeMatch = content.substring(Math.max(0, expMatch.index - 500), expMatch.index);
|
|
206
|
-
const afterMatch = content.substring(expMatch.index, Math.min(content.length, expMatch.index + 200));
|
|
207
|
-
|
|
208
|
-
// Check if it's already in useMemo or useCallback
|
|
209
|
-
if (beforeMatch.includes('useMemo') || beforeMatch.includes('useCallback')) {
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Check if it's inside a function definition that's static (like aggregateFn, cell renderer, etc.)
|
|
214
|
-
// These are function definitions that are called by the library, not recalculated on every render
|
|
215
|
-
const isStaticFunctionDefinition = /(aggregateFn|cell|header|footer|render|format|transform)\s*[:=]\s*\(/.test(beforeMatch);
|
|
216
|
-
if (isStaticFunctionDefinition) {
|
|
217
|
-
continue; // Skip static function definitions
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Check if it's inside a const/let declaration that's likely already memoized
|
|
221
|
-
// Pattern: const x = useMemo(() => ... or const x = useCallback(() => ...
|
|
222
|
-
const isInMemoizedConst = /const\s+\w+\s*=\s*(useMemo|useCallback)\s*\(/.test(beforeMatch);
|
|
223
|
-
if (isInMemoizedConst) {
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Check if it's part of a column definition (static, not recalculated)
|
|
228
|
-
const isColumnDefinition = /(accessorKey|header|cell|aggregateFn|aggregateCell)\s*:/.test(beforeMatch);
|
|
229
|
-
if (isColumnDefinition) {
|
|
230
|
-
continue; // Skip column definitions - these are static
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
suggestions.push({
|
|
234
|
-
type: 'expensive-computation',
|
|
235
|
-
file: relativePath,
|
|
236
|
-
line: getLineNumber(content, expMatch.index),
|
|
237
|
-
message: 'Expensive computation detected that could benefit from useMemo',
|
|
238
|
-
recommendation: 'Wrap expensive computations in useMemo to prevent recalculation on every render'
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
// Check for functions passed as props that could use useCallback
|
|
244
|
-
// Only flag complex inline functions, not simple event handlers
|
|
245
|
-
// For library code, be very lenient - inline functions are often acceptable patterns
|
|
246
|
-
if (!isLibraryCode) {
|
|
247
|
-
// Only check for consuming applications, not library code
|
|
248
|
-
const functionPropPattern = /(onClick|onChange|onSubmit|onFocus|onBlur|onMouseEnter|onMouseLeave|onValueChange)\s*=\s*\{[^}]*=>/g;
|
|
249
|
-
let funcMatch;
|
|
250
|
-
while ((funcMatch = functionPropPattern.exec(content)) !== null) {
|
|
251
|
-
const beforeMatch = content.substring(Math.max(0, funcMatch.index - 150), funcMatch.index);
|
|
252
|
-
const afterMatch = content.substring(funcMatch.index, Math.min(content.length, funcMatch.index + 300));
|
|
253
|
-
|
|
254
|
-
// Skip if already using useCallback or defined as const
|
|
255
|
-
if (beforeMatch.includes('useCallback') || beforeMatch.includes('const ')) {
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Extract the function body to check complexity
|
|
260
|
-
// Handle both arrow functions with and without braces
|
|
261
|
-
let funcBody = '';
|
|
262
|
-
const arrowMatch = afterMatch.match(/=>\s*(\{?)([^}]*?)(\}?)/);
|
|
263
|
-
if (arrowMatch) {
|
|
264
|
-
if (arrowMatch[1] === '{') {
|
|
265
|
-
// Multi-line function with braces - extract content between braces
|
|
266
|
-
const braceMatch = afterMatch.match(/=>\s*\{([^}]+)\}/);
|
|
267
|
-
if (braceMatch) {
|
|
268
|
-
funcBody = braceMatch[1];
|
|
269
|
-
}
|
|
270
|
-
} else {
|
|
271
|
-
// Single expression arrow function
|
|
272
|
-
funcBody = arrowMatch[2];
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (funcBody) {
|
|
277
|
-
const trimmedBody = funcBody.trim();
|
|
278
|
-
|
|
279
|
-
// Very short handlers are usually simple
|
|
280
|
-
if (trimmedBody.length < 50) {
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Check for simple setState patterns (even with object spread)
|
|
285
|
-
const isSimpleSetState = /^\s*set\w+\s*\([^)]*\)\s*;?\s*$/.test(trimmedBody) ||
|
|
286
|
-
/^\s*set\w+\s*\([^)]*prev\s*=>\s*\(\{[^}]*\}\)[^}]*\)\s*;?\s*$/.test(trimmedBody);
|
|
287
|
-
|
|
288
|
-
// Check for simple function calls
|
|
289
|
-
const isSimpleFunctionCall = /^\s*(handle\w+|on\w+|navigate|window\.|document\.)\s*\([^)]*\)\s*;?\s*$/.test(trimmedBody);
|
|
290
|
-
|
|
291
|
-
// Check for simple conditional (single if/ternary)
|
|
292
|
-
const isSimpleConditional = /^\s*(if\s*\([^)]+\)\s*\{[^}]{0,50}\}|[^?]+\?[^:]+:[^;]+;?)\s*$/.test(trimmedBody);
|
|
293
|
-
|
|
294
|
-
// Check for simple value transformations (common in library components)
|
|
295
|
-
// Pattern: (e) => handleFunc(e.target.value || undefined)
|
|
296
|
-
// Pattern: (e) => handleFunc(e.target.value ? transform(e.target.value) : undefined)
|
|
297
|
-
const isSimpleValueTransform = /^\s*\w+\s*\([^)]*\.(target|value|checked|selected)[^)]*\)\s*;?\s*$/.test(trimmedBody) ||
|
|
298
|
-
/^\s*\w+\s*\([^)]*\?[^:]+:[^;]+\)\s*;?\s*$/.test(trimmedBody);
|
|
299
|
-
|
|
300
|
-
// Skip simple handlers - they're fine as inline functions
|
|
301
|
-
if (isSimpleSetState || isSimpleFunctionCall || isSimpleConditional || isSimpleValueTransform) {
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
suggestions.push({
|
|
307
|
-
type: 'inline-function-prop',
|
|
308
|
-
file: relativePath,
|
|
309
|
-
line: getLineNumber(content, funcMatch.index),
|
|
310
|
-
message: 'Complex inline function passed as prop',
|
|
311
|
-
recommendation: 'Use useCallback to memoize complex functions passed as props to prevent child re-renders. Simple event handlers (single setState call or function invocation) are fine as inline functions.'
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
// For library code, skip this check entirely - inline functions are acceptable patterns
|
|
316
|
-
} catch (error) {
|
|
317
|
-
// Skip files with errors
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return { issues, warnings, suggestions };
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
module.exports = performanceCheck;
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Route Protection Check Module
|
|
5
|
-
* @package @jmruthers/pace-core
|
|
6
|
-
* @module Audit/Checks/Routes
|
|
7
|
-
*
|
|
8
|
-
* Checks for:
|
|
9
|
-
* - Routes without PagePermissionGuard
|
|
10
|
-
* - Incorrect route nesting
|
|
11
|
-
* - Missing route error boundaries
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const fs = require('fs');
|
|
15
|
-
const { getRelativePath, getLineNumber } = require('../utils.cjs');
|
|
16
|
-
|
|
17
|
-
const routesCheck = {
|
|
18
|
-
name: 'routes',
|
|
19
|
-
description: 'Route protection (PagePermissionGuard, route nesting)',
|
|
20
|
-
severity: 'error',
|
|
21
|
-
|
|
22
|
-
async run(context) {
|
|
23
|
-
const { projectRoot, files } = context;
|
|
24
|
-
const issues = [];
|
|
25
|
-
const warnings = [];
|
|
26
|
-
const suggestions = [];
|
|
27
|
-
|
|
28
|
-
if (!files || files.length === 0) {
|
|
29
|
-
return { issues, warnings, suggestions };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
for (const filePath of files) {
|
|
33
|
-
try {
|
|
34
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
35
|
-
const relativePath = getRelativePath(filePath, projectRoot);
|
|
36
|
-
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
37
|
-
|
|
38
|
-
// Skip pace-core package files - routes check is for consuming applications, not the library itself
|
|
39
|
-
// Note: Library components (like ProtectedRoute, PaceAppLayout) ARE the route protection components
|
|
40
|
-
// They don't need to use PagePermissionGuard - they provide the protection functionality
|
|
41
|
-
const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
|
|
42
|
-
if (isPaceCorePackage) {
|
|
43
|
-
continue; // Skip library files (including examples)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Skip root-level src directory - in pace-core repository, this is a demo/showcase app
|
|
47
|
-
const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
|
|
48
|
-
if (isRootSrc) {
|
|
49
|
-
continue; // Skip demo app files
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Skip scripts directory - utility scripts don't need route protection validation
|
|
53
|
-
const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
|
|
54
|
-
if (isScript) {
|
|
55
|
-
continue; // Skip script files
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Check for route definitions
|
|
59
|
-
const routePatterns = [
|
|
60
|
-
/<Route\s+path=["'][^"']+["']/g,
|
|
61
|
-
/<Route\s+element\s*=/g,
|
|
62
|
-
/createBrowserRouter\s*\(/g,
|
|
63
|
-
/createRoutesFromElements/g
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
const hasRoutes = routePatterns.some(pattern => pattern.test(content));
|
|
67
|
-
|
|
68
|
-
if (hasRoutes) {
|
|
69
|
-
// Check for PagePermissionGuard
|
|
70
|
-
const hasPagePermissionGuard = content.includes('PagePermissionGuard') ||
|
|
71
|
-
content.includes('from \'@jmruthers/pace-core/rbac\'');
|
|
72
|
-
|
|
73
|
-
if (!hasPagePermissionGuard && !relativePath.includes('test') && !relativePath.includes('spec')) {
|
|
74
|
-
issues.push({
|
|
75
|
-
type: 'unprotected-route',
|
|
76
|
-
file: relativePath,
|
|
77
|
-
message: 'Route file found without PagePermissionGuard',
|
|
78
|
-
recommendation: 'Wrap routes with PagePermissionGuard from @jmruthers/pace-core/rbac to enforce permissions'
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Check for route nesting issues
|
|
83
|
-
const routeCount = (content.match(/<Route/g) || []).length;
|
|
84
|
-
if (routeCount > 0) {
|
|
85
|
-
// Check if routes are properly nested
|
|
86
|
-
const hasRoutesWrapper = content.includes('<Routes') || content.includes('<BrowserRouter');
|
|
87
|
-
if (!hasRoutesWrapper) {
|
|
88
|
-
warnings.push({
|
|
89
|
-
type: 'route-nesting',
|
|
90
|
-
file: relativePath,
|
|
91
|
-
message: 'Route elements found but may not be wrapped in <Routes>',
|
|
92
|
-
recommendation: 'Ensure all Route elements are wrapped in <Routes> component'
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Check for error boundaries around routes
|
|
99
|
-
if (hasRoutes && !content.includes('ErrorBoundary') && !content.includes('error-boundary')) {
|
|
100
|
-
suggestions.push({
|
|
101
|
-
type: 'missing-route-error-boundary',
|
|
102
|
-
file: relativePath,
|
|
103
|
-
message: 'Routes without error boundary',
|
|
104
|
-
recommendation: 'Wrap routes with ErrorBoundary to catch and handle route errors gracefully'
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
} catch (error) {
|
|
109
|
-
// Skip files with errors
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return { issues, warnings, suggestions };
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
module.exports = routesCheck;
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* State Management Check Module
|
|
5
|
-
* @package @jmruthers/pace-core
|
|
6
|
-
* @module Audit/Checks/State
|
|
7
|
-
*
|
|
8
|
-
* Checks for:
|
|
9
|
-
* - Improper state lifting
|
|
10
|
-
* - Prop drilling (too many levels)
|
|
11
|
-
* - Missing state normalization
|
|
12
|
-
* - Unnecessary global state
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const { getRelativePath, getLineNumber } = require('../utils.cjs');
|
|
17
|
-
|
|
18
|
-
const stateCheck = {
|
|
19
|
-
name: 'state',
|
|
20
|
-
description: 'State management patterns (prop drilling, state lifting)',
|
|
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
|
-
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 pace-core package files - state check is for consuming applications, not the library itself
|
|
45
|
-
// Note: Library components are designed to have many props for configurability - this is not prop drilling
|
|
46
|
-
const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
|
|
47
|
-
if (isPaceCorePackage) {
|
|
48
|
-
continue; // Skip library files
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Skip root-level src directory - in pace-core repository, this is a demo/showcase app
|
|
52
|
-
const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
|
|
53
|
-
if (isRootSrc) {
|
|
54
|
-
continue; // Skip demo app files
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Skip scripts directory - utility scripts don't need state management validation
|
|
58
|
-
const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
|
|
59
|
-
if (isScript) {
|
|
60
|
-
continue; // Skip script files
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Check for prop drilling (component with many props that are just passed through)
|
|
64
|
-
const componentPattern = /(?:function|const)\s+(\w+)\s*[=\(]\s*\([^)]*\)/g;
|
|
65
|
-
let match;
|
|
66
|
-
while ((match = componentPattern.exec(content)) !== null) {
|
|
67
|
-
const propsMatch = match[0].match(/\(([^)]+)\)/);
|
|
68
|
-
if (propsMatch) {
|
|
69
|
-
const props = propsMatch[1].split(',').map(p => p.trim()).filter(p => p);
|
|
70
|
-
|
|
71
|
-
// Check if component has many props (potential prop drilling)
|
|
72
|
-
if (props.length > 8) {
|
|
73
|
-
suggestions.push({
|
|
74
|
-
type: 'many-props',
|
|
75
|
-
file: relativePath,
|
|
76
|
-
line: getLineNumber(content, match.index),
|
|
77
|
-
message: `Component has ${props.length} props - may indicate prop drilling`,
|
|
78
|
-
recommendation: 'Consider using context or state management library if props are passed through multiple levels'
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Check if props are just passed through to children
|
|
83
|
-
const componentBody = content.substring(match.index, Math.min(content.length, match.index + 1000));
|
|
84
|
-
const passThroughProps = props.filter(prop => {
|
|
85
|
-
const propName = prop.split(':')[0].trim();
|
|
86
|
-
return new RegExp(`<\\w+\\s+[^>]*${propName}=`, 'g').test(componentBody);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
if (passThroughProps.length > props.length * 0.7 && props.length > 3) {
|
|
90
|
-
suggestions.push({
|
|
91
|
-
type: 'prop-drilling',
|
|
92
|
-
file: relativePath,
|
|
93
|
-
line: getLineNumber(content, match.index),
|
|
94
|
-
message: 'Component appears to be passing most props through to children',
|
|
95
|
-
recommendation: 'Consider lifting state up or using React Context to avoid prop drilling'
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Check for unnecessary useState when data comes from props
|
|
102
|
-
const useStatePattern = /const\s+\[(\w+),\s*set\w+\]\s*=\s*useState/g;
|
|
103
|
-
let stateMatch;
|
|
104
|
-
while ((stateMatch = useStatePattern.exec(content)) !== null) {
|
|
105
|
-
const stateVar = stateMatch[1];
|
|
106
|
-
const beforeState = content.substring(Math.max(0, stateMatch.index - 200), stateMatch.index);
|
|
107
|
-
|
|
108
|
-
// Check if there's a prop with the same name
|
|
109
|
-
const propPattern = new RegExp(`(?:props\\.|\\{\\s*${stateVar}\\s*\\})`, 'g');
|
|
110
|
-
if (propPattern.test(beforeState)) {
|
|
111
|
-
suggestions.push({
|
|
112
|
-
type: 'unnecessary-state',
|
|
113
|
-
file: relativePath,
|
|
114
|
-
line: getLineNumber(content, stateMatch.index),
|
|
115
|
-
message: `State variable '${stateVar}' may duplicate a prop`,
|
|
116
|
-
recommendation: 'If state comes from props, consider using the prop directly or use derived state pattern'
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
} catch (error) {
|
|
122
|
-
// Skip files with errors
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return { issues, warnings, suggestions };
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
module.exports = stateCheck;
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* File Structure Check Module
|
|
5
|
-
* @package @jmruthers/pace-core
|
|
6
|
-
* @module Audit/Checks/Structure
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
|
|
12
|
-
const structureCheck = {
|
|
13
|
-
name: 'structure',
|
|
14
|
-
description: 'File structure checks (required/recommended directories)',
|
|
15
|
-
severity: 'error',
|
|
16
|
-
|
|
17
|
-
async run(context) {
|
|
18
|
-
const { projectRoot } = context;
|
|
19
|
-
const issues = [];
|
|
20
|
-
const warnings = [];
|
|
21
|
-
|
|
22
|
-
// Skip structure checks if this is the pace-core repository itself
|
|
23
|
-
// Structure checks are for consuming applications, not the library repository
|
|
24
|
-
// Detect pace-core repository by checking if packages/core exists
|
|
25
|
-
const packagesCorePath = path.join(projectRoot, 'packages', 'core');
|
|
26
|
-
const isPaceCoreRepository = fs.existsSync(packagesCorePath);
|
|
27
|
-
|
|
28
|
-
if (isPaceCoreRepository) {
|
|
29
|
-
// This is the pace-core library repository, not a consuming app
|
|
30
|
-
// Skip structure checks - the library has its own structure
|
|
31
|
-
return { issues: [], warnings: [], suggestions: [] };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const requiredDirs = ['src'];
|
|
35
|
-
const recommendedDirs = ['src/components', 'src/hooks', 'src/utils', 'src/pages'];
|
|
36
|
-
|
|
37
|
-
requiredDirs.forEach(dir => {
|
|
38
|
-
const dirPath = path.join(projectRoot, dir);
|
|
39
|
-
if (!fs.existsSync(dirPath)) {
|
|
40
|
-
issues.push({
|
|
41
|
-
type: 'missing-directory',
|
|
42
|
-
file: dir,
|
|
43
|
-
message: `Required directory '${dir}' is missing`,
|
|
44
|
-
recommendation: `Create the '${dir}' directory`
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
recommendedDirs.forEach(dir => {
|
|
50
|
-
const dirPath = path.join(projectRoot, dir);
|
|
51
|
-
if (!fs.existsSync(dirPath)) {
|
|
52
|
-
warnings.push({
|
|
53
|
-
type: 'missing-directory',
|
|
54
|
-
file: dir,
|
|
55
|
-
message: `Recommended directory '${dir}' is missing`,
|
|
56
|
-
recommendation: `Consider creating the '${dir}' directory for better organization`
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
return { issues, warnings, suggestions: [] };
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
module.exports = structureCheck;
|