@jmruthers/pace-core 0.6.4 → 0.6.6
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/core-usage-manifest.json +93 -0
- package/cursor-rules/00-pace-core-compliance.mdc +128 -26
- package/cursor-rules/01-standards-compliance.mdc +49 -8
- package/cursor-rules/02-project-structure.mdc +6 -0
- package/cursor-rules/03-solid-principles.mdc +2 -0
- package/cursor-rules/04-testing-standards.mdc +2 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +2 -0
- package/cursor-rules/06-code-quality.mdc +2 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +2 -0
- package/cursor-rules/08-markup-quality.mdc +52 -27
- package/cursor-rules/09-rbac-compliance.mdc +462 -0
- package/cursor-rules/10-error-handling-patterns.mdc +179 -0
- package/cursor-rules/11-performance-optimization.mdc +169 -0
- package/cursor-rules/12-ci-cd-integration.mdc +150 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-2N_tqbfq.d.ts} +1 -1
- package/dist/DataTable-LRJL4IRV.js +15 -0
- package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-BBH6Vqg7.d.ts} +72 -139
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-J36DSWQK.js → chunk-2HGJFNAH.js} +8 -28
- package/dist/{chunk-OEWDTMG7.js → chunk-3O3WHILE.js} +38 -121
- package/dist/{chunk-M43Y4SSO.js → chunk-3QC3KRHK.js} +1 -14
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/chunk-4T7OBVTU.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-NN6WWZ5U.js → chunk-7TYHROIV.js} +579 -563
- package/dist/{chunk-M7MPQISP.js → chunk-A55DK444.js} +9 -16
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-L4OXEN46.js → chunk-BVP2BCJF.js} +2 -16
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-YKRAFF5K.js → chunk-FENMYN2U.js} +73 -149
- package/dist/{chunk-AVMLPIM7.js → chunk-FTCRZOG2.js} +284 -432
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-I6DAQMWX.js → chunk-LAZMKTTF.js} +930 -891
- package/dist/{chunk-5EC5MEWX.js → chunk-MAGBIDNS.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/chunk-OHIK3MIO.js +994 -0
- package/dist/{chunk-6SOIHG6Z.js → chunk-S7DKJPLT.js} +115 -44
- package/dist/{chunk-FMUCXFII.js → chunk-SD6WQY43.js} +1 -5
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-FFQEQTNW.js → chunk-UIYSCEV7.js} +134 -45
- package/dist/{chunk-3LPHPB62.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-7JPAB3T5.js → chunk-ZS5VO5JB.js} +1989 -1283
- package/dist/components.d.ts +6 -6
- package/dist/components.js +57 -267
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +22 -0
- package/dist/eslint-rules/rules/compliance.cjs +348 -0
- package/dist/eslint-rules/rules/components.cjs +113 -0
- package/dist/eslint-rules/rules/imports.cjs +102 -0
- package/dist/eslint-rules/rules/rbac.cjs +790 -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 +5 -5
- package/dist/hooks.js +62 -270
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +36 -26
- package/dist/index.js +87 -690
- 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 +124 -594
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- 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-B-K_5VnO.d.ts} +4 -0
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-COZ28Mvq.d.ts} +9 -9
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +16 -6
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +454 -930
- package/docs/api-reference/components.md +3 -1
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/best-practices/accessibility.md +6 -3
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/permission-enforcement.md +4 -0
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +12 -3
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/secure-client-protection.md +0 -34
- package/docs/standards/00-pace-core-compliance.md +967 -0
- package/docs/standards/01-standards-compliance.md +188 -0
- package/docs/standards/02-project-structure.md +985 -0
- package/docs/standards/03-solid-principles.md +39 -0
- package/docs/standards/04-testing-standards.md +36 -0
- package/docs/standards/05-bug-reports-and-features.md +27 -0
- package/docs/standards/{04-code-style-standard.md → 06-code-quality.md} +2 -0
- package/docs/standards/07-tech-stack-compliance.md +30 -0
- package/docs/standards/08-markup-quality.md +345 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 09-rbac-compliance.md} +149 -54
- package/docs/standards/10-error-handling-patterns.md +401 -0
- package/docs/standards/11-performance-optimization.md +348 -0
- package/docs/standards/12-ci-cd-integration.md +370 -0
- package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +192 -0
- package/docs/standards/README.md +62 -33
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +20 -4
- package/package.json +31 -21
- package/scripts/audit/audit-compliance.cjs +1295 -0
- package/scripts/audit/audit-components.cjs +260 -0
- package/scripts/audit/audit-dependencies.cjs +395 -0
- package/scripts/audit/audit-rbac.cjs +954 -0
- package/scripts/audit/audit-standards.cjs +1268 -0
- package/scripts/audit/index.cjs +1898 -194
- package/scripts/install-cursor-rules.cjs +259 -8
- package/scripts/validate-master.js +1 -1
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +1 -1
- 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-utils.test.tsx +3 -3
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/ContextSelector/ContextSelector.tsx +42 -39
- package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
- package/src/components/DataTable/components/DataTableBody.tsx +55 -31
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableLayout.tsx +30 -5
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +7 -2
- package/src/components/DataTable/components/ImportModal.tsx +4 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/core/DataTableContext.tsx +1 -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 -0
- package/src/components/DateTimeField/DateTimeField.tsx +20 -20
- package/src/components/DateTimeField/README.md +5 -2
- package/src/components/Dialog/Dialog.test.tsx +361 -318
- package/src/components/Dialog/Dialog.tsx +1154 -323
- package/src/components/Dialog/index.ts +3 -3
- package/src/components/FileDisplay/FileDisplay.test.tsx +45 -2
- package/src/components/FileDisplay/FileDisplay.tsx +28 -22
- package/src/components/Form/Form.test.tsx +9 -10
- package/src/components/Form/Form.tsx +369 -9
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
- package/src/components/LoginForm/LoginForm.tsx +2 -2
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +30 -41
- package/src/components/PaceAppLayout/README.md +10 -9
- package/src/components/PaceAppLayout/test-setup.tsx +40 -31
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
- package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
- package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
- package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
- package/src/components/Select/Select.tsx +23 -21
- package/src/components/Select/types.ts +1 -1
- package/src/components/UserMenu/UserMenu.test.tsx +38 -6
- package/src/components/UserMenu/UserMenu.tsx +39 -34
- package/src/components/index.ts +3 -4
- package/src/eslint-rules/index.cjs +22 -0
- package/src/eslint-rules/rules/compliance.cjs +348 -0
- package/src/eslint-rules/rules/components.cjs +113 -0
- package/src/eslint-rules/rules/imports.cjs +102 -0
- package/src/eslint-rules/rules/rbac.cjs +790 -0
- package/src/eslint-rules/utils/helpers.cjs +42 -0
- package/src/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/src/hooks/__tests__/hooks.integration.test.tsx +6 -8
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- 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 +62 -190
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +19 -9
- package/src/hooks/useAppConfig.ts +26 -24
- package/src/hooks/useEventTheme.test.ts +211 -233
- package/src/hooks/useEventTheme.ts +19 -28
- package/src/hooks/useEvents.ts +11 -7
- package/src/hooks/useKeyboardShortcuts.ts +1 -1
- package/src/hooks/useOrganisationPermissions.ts +9 -11
- package/src/hooks/useOrganisations.ts +13 -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 +16 -1
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -0
- package/src/rbac/README.md +20 -20
- 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/useRBAC.test.ts +21 -3
- package/src/rbac/hooks/useRBAC.ts +4 -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 +241 -60
- package/src/rbac/hooks/useResourcePermissions.ts +182 -63
- 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/permissions.ts +17 -17
- package/src/rbac/utils/contextValidator.ts +45 -7
- package/src/services/AuthService.ts +132 -23
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +155 -58
- 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 +4 -0
- package/src/types/database.generated.ts +4733 -3809
- 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/formatting/formatTime.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/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/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-E7YQZD7D.js +0 -175
- package/dist/DataTable-E7YQZD7D.js.map +0 -1
- package/dist/UnifiedAuthProvider-QPXO24B4.js +0 -18
- package/dist/UnifiedAuthProvider-QPXO24B4.js.map +0 -1
- package/dist/api-6LVZTHDS.js +0 -52
- package/dist/api-6LVZTHDS.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-36LVWXB2.js +0 -227
- package/dist/chunk-36LVWXB2.js.map +0 -1
- package/dist/chunk-3LPHPB62.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-5EC5MEWX.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6SOIHG6Z.js.map +0 -1
- package/dist/chunk-7JPAB3T5.js.map +0 -1
- package/dist/chunk-ATKZM7RX.js +0 -2053
- package/dist/chunk-ATKZM7RX.js.map +0 -1
- package/dist/chunk-AVMLPIM7.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.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-I6DAQMWX.js.map +0 -1
- package/dist/chunk-J36DSWQK.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-NN6WWZ5U.js.map +0 -1
- package/dist/chunk-OEWDTMG7.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-YKRAFF5K.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-OOPCLPZW.js +0 -9
- package/dist/contextValidator-OOPCLPZW.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/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/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/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/eslint-rules/pace-core-compliance.cjs +0 -510
- 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
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Component Usage Audit Script
|
|
5
|
+
*
|
|
6
|
+
* MIGRATED TO ESLINT: All component usage checks have been migrated to ESLint rules:
|
|
7
|
+
* - Plain <form> tags → ESLint: prefer-pace-core-form
|
|
8
|
+
* - Direct react-hook-form usage → ESLint: prefer-pace-core-form
|
|
9
|
+
*
|
|
10
|
+
* This file is kept for reference but is no longer actively used.
|
|
11
|
+
* Use ESLint rules for real-time feedback during development.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const { runComponentAudit } = require('./audit-components.cjs');
|
|
15
|
+
* const issues = runComponentAudit(consumingAppPath);
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Recursively find all TypeScript/JavaScript files in a directory
|
|
23
|
+
*/
|
|
24
|
+
function findSourceFiles(dir, fileList = []) {
|
|
25
|
+
const files = fs.readdirSync(dir);
|
|
26
|
+
|
|
27
|
+
files.forEach(file => {
|
|
28
|
+
const filePath = path.join(dir, file);
|
|
29
|
+
const stat = fs.statSync(filePath);
|
|
30
|
+
|
|
31
|
+
if (stat.isDirectory()) {
|
|
32
|
+
// Skip node_modules, dist, build, .git, etc.
|
|
33
|
+
if (!['node_modules', 'dist', 'build', '.git', '.next', '.vite', 'coverage'].includes(file)) {
|
|
34
|
+
findSourceFiles(filePath, fileList);
|
|
35
|
+
}
|
|
36
|
+
} else if (/\.(ts|tsx|js|jsx)$/.test(file)) {
|
|
37
|
+
fileList.push(filePath);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return fileList;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get line number from index in content
|
|
46
|
+
*/
|
|
47
|
+
function getLineNumber(content, index) {
|
|
48
|
+
return content.substring(0, index).split('\n').length;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if a file imports Form from pace-core
|
|
53
|
+
*/
|
|
54
|
+
function importsPaceCoreForm(content) {
|
|
55
|
+
// Check for imports like:
|
|
56
|
+
// import { Form } from '@jmruthers/pace-core
|
|
57
|
+
// import { Form } from '@jmruthers/pace-core/components'
|
|
58
|
+
// import Form from '@jmruthers/pace-core'
|
|
59
|
+
const paceCoreFormPattern = /from\s+['"]@jmruthers\/pace-core(?:\/components)?['"].*Form|import\s+.*Form.*from\s+['"]@jmruthers\/pace-core/;
|
|
60
|
+
return paceCoreFormPattern.test(content);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check for plain <form> tags in JSX
|
|
65
|
+
*
|
|
66
|
+
* MIGRATED TO ESLINT: This check is now handled by 'prefer-pace-core-form' ESLint rule.
|
|
67
|
+
* Kept for reference only.
|
|
68
|
+
*/
|
|
69
|
+
function checkPlainFormTags_MIGRATED_TO_ESLINT(content, filePath, consumingAppPath) {
|
|
70
|
+
const issues = [];
|
|
71
|
+
|
|
72
|
+
// Pattern to match <form> tags (but not <Form> component)
|
|
73
|
+
// This regex looks for <form followed by whitespace or attributes, but not <Form
|
|
74
|
+
const formTagPattern = /<form(\s|>)/g;
|
|
75
|
+
let match;
|
|
76
|
+
|
|
77
|
+
while ((match = formTagPattern.exec(content)) !== null) {
|
|
78
|
+
// Check if this is actually a Form component (capital F)
|
|
79
|
+
const beforeMatch = content.substring(Math.max(0, match.index - 10), match.index);
|
|
80
|
+
const afterMatch = content.substring(match.index, match.index + 20);
|
|
81
|
+
|
|
82
|
+
// Skip if it's a comment or string literal
|
|
83
|
+
const beforeContext = content.substring(0, match.index);
|
|
84
|
+
const lastComment = beforeContext.lastIndexOf('//');
|
|
85
|
+
const lastNewline = beforeContext.lastIndexOf('\n');
|
|
86
|
+
const isInComment = lastComment > lastNewline && !beforeContext.substring(lastComment, match.index).includes('\n');
|
|
87
|
+
|
|
88
|
+
if (isInComment) continue;
|
|
89
|
+
|
|
90
|
+
// Check if it's inside a string literal
|
|
91
|
+
const beforeQuote = beforeContext.lastIndexOf('"');
|
|
92
|
+
const afterQuote = content.indexOf('"', match.index);
|
|
93
|
+
const isInString = beforeQuote > lastNewline && afterQuote > match.index && beforeQuote < match.index;
|
|
94
|
+
|
|
95
|
+
if (isInString) continue;
|
|
96
|
+
|
|
97
|
+
// This is a plain <form> tag
|
|
98
|
+
// Get relative path from consuming app root
|
|
99
|
+
const relativePath = path.relative(consumingAppPath || process.cwd(), filePath);
|
|
100
|
+
|
|
101
|
+
issues.push({
|
|
102
|
+
type: 'plainFormTag',
|
|
103
|
+
file: relativePath,
|
|
104
|
+
line: getLineNumber(content, match.index),
|
|
105
|
+
message: 'Plain <form> tag detected. Use pace-core Form component instead.',
|
|
106
|
+
code: content.substring(Math.max(0, match.index - 30), Math.min(content.length, match.index + 50)).trim(),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return issues;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check for direct react-hook-form imports and usage
|
|
115
|
+
*
|
|
116
|
+
* MIGRATED TO ESLINT: This check is now handled by 'prefer-pace-core-form' ESLint rule.
|
|
117
|
+
* Kept for reference only.
|
|
118
|
+
*/
|
|
119
|
+
function checkReactHookFormUsage_MIGRATED_TO_ESLINT(content, filePath, consumingAppPath) {
|
|
120
|
+
const issues = [];
|
|
121
|
+
|
|
122
|
+
// Pattern to match imports from react-hook-form
|
|
123
|
+
const importPattern = /from\s+['"]react-hook-form['"]/g;
|
|
124
|
+
let match;
|
|
125
|
+
|
|
126
|
+
while ((match = importPattern.exec(content)) !== null) {
|
|
127
|
+
// Get the import statement
|
|
128
|
+
const lineStart = content.lastIndexOf('\n', match.index) + 1;
|
|
129
|
+
const lineEnd = content.indexOf('\n', match.index);
|
|
130
|
+
const importLine = content.substring(lineStart, lineEnd === -1 ? content.length : lineEnd);
|
|
131
|
+
|
|
132
|
+
// Check if this file also imports Form from pace-core
|
|
133
|
+
// If it does, this might be acceptable (e.g., using useFormContext within Form)
|
|
134
|
+
const hasPaceCoreForm = importsPaceCoreForm(content);
|
|
135
|
+
|
|
136
|
+
// Extract what's being imported
|
|
137
|
+
const importMatch = importLine.match(/import\s+(?:{([^}]+)}|(\w+))\s+from/);
|
|
138
|
+
if (importMatch) {
|
|
139
|
+
const importedItems = importMatch[1] || importMatch[2] || '';
|
|
140
|
+
const items = importedItems.split(',').map(item => item.trim().split(' as ')[0].trim());
|
|
141
|
+
|
|
142
|
+
// Check for problematic imports
|
|
143
|
+
const problematicImports = [
|
|
144
|
+
'useForm',
|
|
145
|
+
'FormProvider',
|
|
146
|
+
'Controller',
|
|
147
|
+
'useFormContext',
|
|
148
|
+
'useWatch',
|
|
149
|
+
'useFieldArray',
|
|
150
|
+
'useController',
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
const foundProblematic = items.filter(item => problematicImports.includes(item));
|
|
154
|
+
|
|
155
|
+
if (foundProblematic.length > 0) {
|
|
156
|
+
// If they're using useFormContext within a pace-core Form, that's acceptable
|
|
157
|
+
// But if they're using useForm or FormProvider, that's not acceptable
|
|
158
|
+
const isAcceptable = hasPaceCoreForm &&
|
|
159
|
+
foundProblematic.length === 1 &&
|
|
160
|
+
foundProblematic[0] === 'useFormContext';
|
|
161
|
+
|
|
162
|
+
if (!isAcceptable) {
|
|
163
|
+
// Get relative path from consuming app root
|
|
164
|
+
const relativePath = path.relative(consumingAppPath || process.cwd(), filePath);
|
|
165
|
+
|
|
166
|
+
issues.push({
|
|
167
|
+
type: 'reactHookFormImport',
|
|
168
|
+
file: relativePath,
|
|
169
|
+
line: getLineNumber(content, match.index),
|
|
170
|
+
message: `Direct import from 'react-hook-form' detected: ${foundProblematic.join(', ')}. Use pace-core Form component instead.`,
|
|
171
|
+
code: importLine.trim(),
|
|
172
|
+
imports: foundProblematic,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return issues;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Main audit function - scans source files for component usage issues
|
|
184
|
+
*/
|
|
185
|
+
function runComponentAudit(consumingAppPath = process.cwd()) {
|
|
186
|
+
const srcPath = path.join(consumingAppPath, 'src');
|
|
187
|
+
|
|
188
|
+
// If src directory doesn't exist, try root directory
|
|
189
|
+
const searchPath = fs.existsSync(srcPath) ? srcPath : consumingAppPath;
|
|
190
|
+
|
|
191
|
+
if (!fs.existsSync(searchPath)) {
|
|
192
|
+
return {
|
|
193
|
+
error: `Source directory not found at ${searchPath}`,
|
|
194
|
+
issues: { formIssues: [] },
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Find all source files
|
|
199
|
+
const sourceFiles = findSourceFiles(searchPath);
|
|
200
|
+
|
|
201
|
+
if (sourceFiles.length === 0) {
|
|
202
|
+
return {
|
|
203
|
+
error: `No source files found in ${searchPath}`,
|
|
204
|
+
issues: { formIssues: [] },
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const issues = {
|
|
209
|
+
formIssues: [],
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// NOTE: All component checks have been migrated to ESLint rules.
|
|
213
|
+
// This function now returns empty results as checks are handled by ESLint.
|
|
214
|
+
// Kept for API compatibility.
|
|
215
|
+
sourceFiles.forEach(filePath => {
|
|
216
|
+
// No checks performed - all migrated to ESLint
|
|
217
|
+
// (checkPlainFormTags and checkReactHookFormUsage are now ESLint rules)
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
issues,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Export for use by other scripts
|
|
226
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
227
|
+
module.exports = { runComponentAudit };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// If run directly, output results
|
|
231
|
+
if (require.main === module) {
|
|
232
|
+
const consumingAppPath = process.argv[2] || process.cwd();
|
|
233
|
+
const result = runComponentAudit(consumingAppPath);
|
|
234
|
+
|
|
235
|
+
if (result.error) {
|
|
236
|
+
console.error(`Error: ${result.error}`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const { issues } = result;
|
|
241
|
+
|
|
242
|
+
if (issues.formIssues.length === 0) {
|
|
243
|
+
console.log('✅ No form usage issues found!');
|
|
244
|
+
process.exit(0);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
console.log(`\n❌ Found ${issues.formIssues.length} form usage issue(s):\n`);
|
|
248
|
+
|
|
249
|
+
issues.formIssues.forEach(issue => {
|
|
250
|
+
console.log(`${issue.file}:${issue.line}`);
|
|
251
|
+
console.log(` ${issue.message}`);
|
|
252
|
+
if (issue.code) {
|
|
253
|
+
console.log(` Code: ${issue.code.substring(0, 80)}...`);
|
|
254
|
+
}
|
|
255
|
+
console.log();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dependency Audit Script
|
|
5
|
+
*
|
|
6
|
+
* Audits consuming apps against pace-core dependency standards.
|
|
7
|
+
* Checks for:
|
|
8
|
+
* - Included dependencies that should NOT be installed
|
|
9
|
+
* - Missing required peer dependencies
|
|
10
|
+
* - Incorrect version ranges
|
|
11
|
+
* - Dependencies in wrong location (devDependencies vs dependencies)
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* node scripts/audit/audit-dependencies.cjs [path-to-consuming-app]
|
|
15
|
+
*
|
|
16
|
+
* If no path provided, assumes current directory is consuming app.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
// Colors for terminal output
|
|
23
|
+
const colors = {
|
|
24
|
+
reset: '\x1b[0m',
|
|
25
|
+
red: '\x1b[31m',
|
|
26
|
+
green: '\x1b[32m',
|
|
27
|
+
yellow: '\x1b[33m',
|
|
28
|
+
blue: '\x1b[34m',
|
|
29
|
+
cyan: '\x1b[36m',
|
|
30
|
+
bold: '\x1b[1m',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Get pace-core package.json
|
|
34
|
+
// When run from consuming app: __dirname is node_modules/@jmruthers/pace-core/scripts/audit
|
|
35
|
+
// When run from pace-core repo: __dirname is packages/core/scripts/audit
|
|
36
|
+
function findPaceCorePackageJson(consumingAppPath) {
|
|
37
|
+
// Try relative to script location first (when in pace-core repo or installed package)
|
|
38
|
+
// Now we're in audit/ subdirectory, so need to go up two levels
|
|
39
|
+
let paceCorePath = path.resolve(__dirname, '../../package.json');
|
|
40
|
+
if (fs.existsSync(paceCorePath)) {
|
|
41
|
+
return paceCorePath;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Try alternative location (if script is in different structure)
|
|
45
|
+
paceCorePath = path.resolve(__dirname, '../../../package.json');
|
|
46
|
+
if (fs.existsSync(paceCorePath)) {
|
|
47
|
+
return paceCorePath;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Try finding from consuming app's node_modules (when run from consuming app)
|
|
51
|
+
if (consumingAppPath) {
|
|
52
|
+
const nodeModulesPath = path.join(consumingAppPath, 'node_modules', '@jmruthers', 'pace-core', 'package.json');
|
|
53
|
+
if (fs.existsSync(nodeModulesPath)) {
|
|
54
|
+
return nodeModulesPath;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Fallback: try from process.argv (for standalone CLI usage)
|
|
59
|
+
const fallbackPath = process.argv[2] || process.cwd();
|
|
60
|
+
const fallbackNodeModules = path.join(fallbackPath, 'node_modules', '@jmruthers', 'pace-core', 'package.json');
|
|
61
|
+
if (fs.existsSync(fallbackNodeModules)) {
|
|
62
|
+
return fallbackNodeModules;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Version range matcher
|
|
69
|
+
function matchesVersionRange(version, range) {
|
|
70
|
+
if (!range) return true;
|
|
71
|
+
|
|
72
|
+
// Simple check for caret ranges (^)
|
|
73
|
+
if (range.startsWith('^')) {
|
|
74
|
+
const major = parseInt(range.slice(1).split('.')[0]);
|
|
75
|
+
const versionMajor = parseInt(version.split('.')[0]);
|
|
76
|
+
return versionMajor >= major;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// For exact matches or other ranges, use semver if available
|
|
80
|
+
// For now, just check if version starts with the range
|
|
81
|
+
return version.startsWith(range.replace(/[\^~]/, ''));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check version compatibility
|
|
85
|
+
function checkVersion(installed, required) {
|
|
86
|
+
if (!required) return { valid: true };
|
|
87
|
+
|
|
88
|
+
// Extract version number from installed (remove ^, ~, etc.)
|
|
89
|
+
const installedVersion = installed.replace(/[\^~]/, '');
|
|
90
|
+
const requiredVersion = required.replace(/[\^~]/, '');
|
|
91
|
+
|
|
92
|
+
// Check major version
|
|
93
|
+
const installedMajor = parseInt(installedVersion.split('.')[0]);
|
|
94
|
+
const requiredMajor = parseInt(requiredVersion.split('.')[0]);
|
|
95
|
+
|
|
96
|
+
if (required.startsWith('^')) {
|
|
97
|
+
return {
|
|
98
|
+
valid: installedMajor >= requiredMajor,
|
|
99
|
+
installed,
|
|
100
|
+
required,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
valid: installedMajor === requiredMajor,
|
|
106
|
+
installed,
|
|
107
|
+
required,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Main audit function - returns data structure for programmatic use
|
|
112
|
+
function runDependencyAudit(consumingAppPath = process.cwd()) {
|
|
113
|
+
const packageJsonPath = path.join(consumingAppPath, 'package.json');
|
|
114
|
+
|
|
115
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
116
|
+
return {
|
|
117
|
+
error: `package.json not found at ${packageJsonPath}`,
|
|
118
|
+
issues: { includedDeps: [], missingRequired: [], missingOptional: [], versionIssues: [], wrongLocation: [] }
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Load pace-core package.json
|
|
123
|
+
const paceCorePath = findPaceCorePackageJson(consumingAppPath);
|
|
124
|
+
if (!paceCorePath) {
|
|
125
|
+
return {
|
|
126
|
+
error: 'Could not find pace-core package.json. Make sure @jmruthers/pace-core is installed in your project.',
|
|
127
|
+
issues: { includedDeps: [], missingRequired: [], missingOptional: [], versionIssues: [], wrongLocation: [] }
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const paceCorePkg = JSON.parse(fs.readFileSync(paceCorePath, 'utf8'));
|
|
132
|
+
const INCLUDED_DEPS = Object.keys(paceCorePkg.dependencies || {});
|
|
133
|
+
const PEER_DEPS = paceCorePkg.peerDependencies || {};
|
|
134
|
+
const REQUIRED_PEERS = ['react', 'react-dom', 'react-router-dom', 'tailwindcss'];
|
|
135
|
+
const OPTIONAL_PEERS = Object.keys(PEER_DEPS).filter(dep => !REQUIRED_PEERS.includes(dep));
|
|
136
|
+
|
|
137
|
+
// Verify pace-core is installed
|
|
138
|
+
const paceCoreInNodeModules = path.join(consumingAppPath, 'node_modules', '@jmruthers', 'pace-core', 'package.json');
|
|
139
|
+
if (!fs.existsSync(paceCoreInNodeModules)) {
|
|
140
|
+
return {
|
|
141
|
+
error: '@jmruthers/pace-core not found in node_modules. Please run: npm install @jmruthers/pace-core',
|
|
142
|
+
issues: { includedDeps: [], missingRequired: [], missingOptional: [], versionIssues: [], wrongLocation: [] }
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const consumingPkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
147
|
+
const allDeps = {
|
|
148
|
+
...(consumingPkg.dependencies || {}),
|
|
149
|
+
...(consumingPkg.devDependencies || {}),
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const issues = {
|
|
153
|
+
includedDeps: [],
|
|
154
|
+
missingRequired: [],
|
|
155
|
+
missingOptional: [],
|
|
156
|
+
versionIssues: [],
|
|
157
|
+
wrongLocation: [],
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Check for included dependencies
|
|
161
|
+
INCLUDED_DEPS.forEach(dep => {
|
|
162
|
+
if (allDeps[dep]) {
|
|
163
|
+
issues.includedDeps.push({
|
|
164
|
+
package: dep,
|
|
165
|
+
installed: allDeps[dep],
|
|
166
|
+
location: consumingPkg.dependencies?.[dep] ? 'dependencies' : 'devDependencies',
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Check for required peer dependencies
|
|
172
|
+
REQUIRED_PEERS.forEach(dep => {
|
|
173
|
+
if (!allDeps[dep]) {
|
|
174
|
+
issues.missingRequired.push({
|
|
175
|
+
package: dep,
|
|
176
|
+
required: PEER_DEPS[dep],
|
|
177
|
+
});
|
|
178
|
+
} else {
|
|
179
|
+
// Check version
|
|
180
|
+
const versionCheck = checkVersion(allDeps[dep], PEER_DEPS[dep]);
|
|
181
|
+
if (!versionCheck.valid) {
|
|
182
|
+
issues.versionIssues.push({
|
|
183
|
+
package: dep,
|
|
184
|
+
installed: versionCheck.installed,
|
|
185
|
+
required: versionCheck.required,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Check for optional peer dependencies (warn if missing, but don't fail)
|
|
192
|
+
OPTIONAL_PEERS.forEach(dep => {
|
|
193
|
+
if (!allDeps[dep]) {
|
|
194
|
+
issues.missingOptional.push({
|
|
195
|
+
package: dep,
|
|
196
|
+
required: PEER_DEPS[dep],
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
// Check version
|
|
200
|
+
const versionCheck = checkVersion(allDeps[dep], PEER_DEPS[dep]);
|
|
201
|
+
if (!versionCheck.valid) {
|
|
202
|
+
issues.versionIssues.push({
|
|
203
|
+
package: dep,
|
|
204
|
+
installed: versionCheck.installed,
|
|
205
|
+
required: versionCheck.required,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Check for @tailwindcss/vite in wrong location
|
|
212
|
+
if (consumingPkg.dependencies?.['@tailwindcss/vite']) {
|
|
213
|
+
issues.wrongLocation.push({
|
|
214
|
+
package: '@tailwindcss/vite',
|
|
215
|
+
current: 'dependencies',
|
|
216
|
+
shouldBe: 'devDependencies',
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Find pace-core version - check dependencies, devDependencies, or installed package
|
|
221
|
+
let paceCoreVersion = consumingPkg.dependencies?.['@jmruthers/pace-core'] ||
|
|
222
|
+
consumingPkg.devDependencies?.['@jmruthers/pace-core'];
|
|
223
|
+
|
|
224
|
+
// If not in package.json, try to read from installed package
|
|
225
|
+
if (!paceCoreVersion) {
|
|
226
|
+
const installedPkgPath = path.join(consumingAppPath, 'node_modules', '@jmruthers', 'pace-core', 'package.json');
|
|
227
|
+
if (fs.existsSync(installedPkgPath)) {
|
|
228
|
+
try {
|
|
229
|
+
const installedPkg = JSON.parse(fs.readFileSync(installedPkgPath, 'utf8'));
|
|
230
|
+
paceCoreVersion = `installed: ${installedPkg.version}`;
|
|
231
|
+
} catch (e) {
|
|
232
|
+
paceCoreVersion = 'installed (version unknown)';
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
paceCoreVersion = 'NOT FOUND';
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
projectName: consumingPkg.name || 'unknown',
|
|
241
|
+
paceCoreVersion,
|
|
242
|
+
issues,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// CLI function - displays results and returns exit code
|
|
247
|
+
function auditDependencies(consumingAppPath = process.cwd()) {
|
|
248
|
+
const result = runDependencyAudit(consumingAppPath);
|
|
249
|
+
|
|
250
|
+
if (result.error) {
|
|
251
|
+
console.error(`${colors.red}Error: ${result.error}${colors.reset}`);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const { projectName, paceCoreVersion, issues } = result;
|
|
256
|
+
|
|
257
|
+
// Report results
|
|
258
|
+
console.log(`\n${colors.bold}${colors.cyan}Dependency Audit Report${colors.reset}`);
|
|
259
|
+
console.log(`${colors.cyan}${'='.repeat(50)}${colors.reset}\n`);
|
|
260
|
+
console.log(`Project: ${colors.bold}${projectName}${colors.reset}`);
|
|
261
|
+
console.log(`pace-core: ${colors.bold}${paceCoreVersion}${colors.reset}\n`);
|
|
262
|
+
|
|
263
|
+
let hasErrors = false;
|
|
264
|
+
|
|
265
|
+
// Included dependencies (errors)
|
|
266
|
+
if (issues.includedDeps.length > 0) {
|
|
267
|
+
hasErrors = true;
|
|
268
|
+
console.log(`${colors.red}❌ INCLUDED DEPENDENCIES (MUST REMOVE):${colors.reset}`);
|
|
269
|
+
issues.includedDeps.forEach(issue => {
|
|
270
|
+
console.log(` - ${colors.red}${issue.package}${colors.reset}@${issue.installed} (in ${issue.location})`);
|
|
271
|
+
console.log(` → Should NOT be installed (already included in pace-core)`);
|
|
272
|
+
});
|
|
273
|
+
console.log();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Missing required dependencies (errors)
|
|
277
|
+
if (issues.missingRequired.length > 0) {
|
|
278
|
+
hasErrors = true;
|
|
279
|
+
console.log(`${colors.red}❌ MISSING REQUIRED DEPENDENCIES:${colors.reset}`);
|
|
280
|
+
issues.missingRequired.forEach(issue => {
|
|
281
|
+
console.log(` - ${colors.red}${issue.package}${colors.reset} (required: ${issue.required})`);
|
|
282
|
+
});
|
|
283
|
+
console.log();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Version issues (errors)
|
|
287
|
+
if (issues.versionIssues.length > 0) {
|
|
288
|
+
hasErrors = true;
|
|
289
|
+
console.log(`${colors.yellow}⚠️ VERSION ISSUES:${colors.reset}`);
|
|
290
|
+
issues.versionIssues.forEach(issue => {
|
|
291
|
+
console.log(` - ${colors.yellow}${issue.package}${colors.reset}`);
|
|
292
|
+
console.log(` Installed: ${issue.installed}`);
|
|
293
|
+
console.log(` Required: ${issue.required}`);
|
|
294
|
+
});
|
|
295
|
+
console.log();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Wrong location (warnings)
|
|
299
|
+
if (issues.wrongLocation.length > 0) {
|
|
300
|
+
console.log(`${colors.yellow}⚠️ WRONG LOCATION:${colors.reset}`);
|
|
301
|
+
issues.wrongLocation.forEach(issue => {
|
|
302
|
+
console.log(` - ${colors.yellow}${issue.package}${colors.reset}`);
|
|
303
|
+
console.log(` Currently in: ${issue.current}`);
|
|
304
|
+
console.log(` Should be in: ${issue.shouldBe}`);
|
|
305
|
+
});
|
|
306
|
+
console.log();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Missing optional dependencies (warnings)
|
|
310
|
+
if (issues.missingOptional.length > 0) {
|
|
311
|
+
console.log(`${colors.blue}ℹ️ MISSING OPTIONAL DEPENDENCIES:${colors.reset}`);
|
|
312
|
+
console.log(` (Only install if you use these features)`);
|
|
313
|
+
issues.missingOptional.forEach(issue => {
|
|
314
|
+
console.log(` - ${colors.blue}${issue.package}${colors.reset} (required: ${issue.required})`);
|
|
315
|
+
});
|
|
316
|
+
console.log();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Success message
|
|
320
|
+
if (!hasErrors && issues.missingOptional.length === 0 && issues.wrongLocation.length === 0) {
|
|
321
|
+
console.log(`${colors.green}✅ All dependencies are correctly configured!${colors.reset}\n`);
|
|
322
|
+
return 0;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Generate fix commands
|
|
326
|
+
if (hasErrors || issues.wrongLocation.length > 0) {
|
|
327
|
+
console.log(`${colors.bold}Fix Commands:${colors.reset}\n`);
|
|
328
|
+
|
|
329
|
+
if (issues.includedDeps.length > 0) {
|
|
330
|
+
const depsToRemove = issues.includedDeps.map(i => i.package).join(' ');
|
|
331
|
+
console.log(`${colors.cyan}# Remove included dependencies${colors.reset}`);
|
|
332
|
+
console.log(`npm uninstall ${depsToRemove}\n`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (issues.missingRequired.length > 0) {
|
|
336
|
+
const depsToInstall = issues.missingRequired
|
|
337
|
+
.map(i => `${i.package}@${i.required}`)
|
|
338
|
+
.join(' ');
|
|
339
|
+
console.log(`${colors.cyan}# Install missing required dependencies${colors.reset}`);
|
|
340
|
+
console.log(`npm install ${depsToInstall}\n`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (issues.versionIssues.length > 0) {
|
|
344
|
+
const depsToFix = issues.versionIssues
|
|
345
|
+
.map(i => `${i.package}@${i.required}`)
|
|
346
|
+
.join(' ');
|
|
347
|
+
console.log(`${colors.cyan}# Fix version ranges${colors.reset}`);
|
|
348
|
+
console.log(`npm install ${depsToFix}\n`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (issues.wrongLocation.length > 0) {
|
|
352
|
+
issues.wrongLocation.forEach(issue => {
|
|
353
|
+
console.log(`${colors.cyan}# Move ${issue.package} to devDependencies${colors.reset}`);
|
|
354
|
+
console.log(`npm uninstall ${issue.package}`);
|
|
355
|
+
console.log(`npm install -D ${issue.package}\n`);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return hasErrors ? 1 : 0;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Export for use by other scripts
|
|
364
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
365
|
+
module.exports = { runDependencyAudit, checkVersion, findPaceCorePackageJson };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Run audit only if this file is executed directly (not required)
|
|
369
|
+
if (require.main === module) {
|
|
370
|
+
// If run from pace-core repo without path, try to detect consuming app in parent directory
|
|
371
|
+
let consumingAppPath = process.argv[2];
|
|
372
|
+
if (!consumingAppPath) {
|
|
373
|
+
const cwd = process.cwd();
|
|
374
|
+
// If we're in pace-core repo, try to find consuming app in parent directory
|
|
375
|
+
if (cwd.includes('pace-core')) {
|
|
376
|
+
// Look for sibling directories that might be consuming apps
|
|
377
|
+
const parentDir = path.resolve(cwd, '../..');
|
|
378
|
+
const possibleApps = ['pace-admin', 'pace-base', 'pace-cake', 'pace-gear', 'pace-mint', 'pace-portal', 'pace-pump', 'pace-trac'];
|
|
379
|
+
for (const app of possibleApps) {
|
|
380
|
+
const appPath = path.join(parentDir, app);
|
|
381
|
+
if (fs.existsSync(path.join(appPath, 'package.json'))) {
|
|
382
|
+
consumingAppPath = appPath;
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Fall back to current directory
|
|
388
|
+
if (!consumingAppPath) {
|
|
389
|
+
consumingAppPath = process.cwd();
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const exitCode = auditDependencies(consumingAppPath);
|
|
393
|
+
process.exit(exitCode);
|
|
394
|
+
}
|
|
395
|
+
|