@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
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard 7: API & Tech Stack Audit
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Audit/Standard7
|
|
5
|
+
*
|
|
6
|
+
* Audits consuming apps for compliance with Standard 7: API & Tech Stack.
|
|
7
|
+
* Validates RPC naming in SQL migrations, tech stack versions, and Vite configuration.
|
|
8
|
+
*
|
|
9
|
+
* Reference: packages/core/docs/standards/7-api-tech-stack-standards.md
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const { findSQLFiles, findConfigFiles, readFileSafe, getRelativePath, findPaceCorePackageJson } = require('../utils/file-utils.cjs');
|
|
15
|
+
const { getLineNumber, getCodeSnippet, isInCommentOrStringSQL } = require('../utils/code-utils.cjs');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check RPC naming in SQL migrations (data_* for reads, app_* for writes)
|
|
19
|
+
*/
|
|
20
|
+
function checkRPCNaming(consumingAppPath) {
|
|
21
|
+
const issues = [];
|
|
22
|
+
|
|
23
|
+
// Find SQL migration files
|
|
24
|
+
const migrationsPath = path.join(consumingAppPath, 'supabase', 'migrations');
|
|
25
|
+
const altMigrationsPath = path.join(consumingAppPath, 'migrations');
|
|
26
|
+
|
|
27
|
+
const migrationsDir = fs.existsSync(migrationsPath) ? migrationsPath :
|
|
28
|
+
(fs.existsSync(altMigrationsPath) ? altMigrationsPath : null);
|
|
29
|
+
|
|
30
|
+
if (!migrationsDir) {
|
|
31
|
+
return issues; // No migrations directory, skip check
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const sqlFiles = findSQLFiles(migrationsDir);
|
|
35
|
+
|
|
36
|
+
sqlFiles.forEach(filePath => {
|
|
37
|
+
try {
|
|
38
|
+
const content = readFileSafe(filePath);
|
|
39
|
+
if (!content) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const relativePath = getRelativePath(filePath, consumingAppPath);
|
|
44
|
+
|
|
45
|
+
// Find CREATE FUNCTION statements for RPCs
|
|
46
|
+
const functionPattern = /CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+(?:public\.)?["']?([^"'\s()]+)["']?\s*\(/gi;
|
|
47
|
+
let match;
|
|
48
|
+
|
|
49
|
+
while ((match = functionPattern.exec(content)) !== null) {
|
|
50
|
+
const functionName = match[1];
|
|
51
|
+
const functionStart = match.index;
|
|
52
|
+
|
|
53
|
+
if (isInCommentOrStringSQL(content, functionStart)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Skip if it's a helper function (starts with check_, get_, is_)
|
|
58
|
+
if (/^(check_|get_|is_)/i.test(functionName)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check if it's a system function (starts with pg_ or other system prefixes)
|
|
63
|
+
if (/^(pg_|information_schema|current_|session_|set_|reset_|show_)/i.test(functionName)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check if it's an RPC (functions that are exposed as RPCs typically follow naming pattern)
|
|
68
|
+
// Skip if it's a helper function (doesn't follow data_* or app_* pattern)
|
|
69
|
+
if (!/^(data_|app_)/.test(functionName)) {
|
|
70
|
+
// Not an RPC, but should follow naming convention
|
|
71
|
+
issues.push({
|
|
72
|
+
type: 'rpcNaming',
|
|
73
|
+
file: relativePath,
|
|
74
|
+
line: getLineNumber(content, functionStart),
|
|
75
|
+
message: `RPC function '${functionName}' does not follow naming convention. Should start with 'data_' (read) or 'app_' (write) prefix.`,
|
|
76
|
+
code: getCodeSnippet(content, functionStart, 0, 100),
|
|
77
|
+
severity: 'error',
|
|
78
|
+
fix: `Rename function to follow pattern: data_${functionName} (for read) or app_${functionName} (for write)`,
|
|
79
|
+
});
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check naming pattern: data_* for reads, app_* for writes
|
|
84
|
+
const readVerbs = ['list', 'get', 'read'];
|
|
85
|
+
const writeVerbs = ['create', 'update', 'delete', 'bulk_create', 'bulk_update'];
|
|
86
|
+
const validVerbs = [...readVerbs, ...writeVerbs];
|
|
87
|
+
|
|
88
|
+
const parts = functionName.split('_');
|
|
89
|
+
const verb = parts[parts.length - 1];
|
|
90
|
+
|
|
91
|
+
if (!validVerbs.includes(verb)) {
|
|
92
|
+
issues.push({
|
|
93
|
+
type: 'rpcNaming',
|
|
94
|
+
file: relativePath,
|
|
95
|
+
line: getLineNumber(content, functionStart),
|
|
96
|
+
message: `RPC '${functionName}' uses invalid verb '${verb}'. Use only: ${validVerbs.join(', ')}`,
|
|
97
|
+
code: getCodeSnippet(content, functionStart, 0, 100),
|
|
98
|
+
severity: 'error',
|
|
99
|
+
fix: `Rename RPC to use valid verb: ${functionName.replace(verb, validVerbs[0])}`,
|
|
100
|
+
});
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check prefix-verb alignment
|
|
105
|
+
const hasDataPrefix = functionName.startsWith('data_');
|
|
106
|
+
const hasAppPrefix = functionName.startsWith('app_');
|
|
107
|
+
const isReadOperation = readVerbs.includes(verb);
|
|
108
|
+
const isWriteOperation = writeVerbs.includes(verb);
|
|
109
|
+
|
|
110
|
+
if (hasDataPrefix && isWriteOperation) {
|
|
111
|
+
issues.push({
|
|
112
|
+
type: 'rpcNaming',
|
|
113
|
+
file: relativePath,
|
|
114
|
+
line: getLineNumber(content, functionStart),
|
|
115
|
+
message: `RPC '${functionName}' has 'data_' prefix but uses write verb '${verb}'. Use 'app_' prefix for write operations.`,
|
|
116
|
+
code: getCodeSnippet(content, functionStart, 0, 100),
|
|
117
|
+
severity: 'error',
|
|
118
|
+
fix: `Rename to app_${functionName.replace('data_', '')}`,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (hasAppPrefix && isReadOperation) {
|
|
123
|
+
issues.push({
|
|
124
|
+
type: 'rpcNaming',
|
|
125
|
+
file: relativePath,
|
|
126
|
+
line: getLineNumber(content, functionStart),
|
|
127
|
+
message: `RPC '${functionName}' has 'app_' prefix but uses read verb '${verb}'. Use 'data_' prefix for read operations.`,
|
|
128
|
+
code: getCodeSnippet(content, functionStart, 0, 100),
|
|
129
|
+
severity: 'error',
|
|
130
|
+
fix: `Rename to data_${functionName.replace('app_', '')}`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
// Skip files that can't be read
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return issues;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check tech stack versions
|
|
144
|
+
*/
|
|
145
|
+
function checkTechStackVersions(consumingAppPath) {
|
|
146
|
+
const issues = [];
|
|
147
|
+
|
|
148
|
+
const packageJsonPath = path.join(consumingAppPath, 'package.json');
|
|
149
|
+
const packageJsonContent = readFileSafe(packageJsonPath);
|
|
150
|
+
|
|
151
|
+
if (!packageJsonContent) {
|
|
152
|
+
return issues;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let packageJson;
|
|
156
|
+
try {
|
|
157
|
+
packageJson = JSON.parse(packageJsonContent);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
return issues;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const allDeps = {
|
|
163
|
+
...(packageJson.dependencies || {}),
|
|
164
|
+
...(packageJson.devDependencies || {}),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Get pace-core package.json for peer dependencies
|
|
168
|
+
const paceCorePath = findPaceCorePackageJson(consumingAppPath);
|
|
169
|
+
if (!paceCorePath) {
|
|
170
|
+
return issues;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const paceCorePkg = JSON.parse(fs.readFileSync(paceCorePath, 'utf8'));
|
|
174
|
+
const peerDeps = paceCorePkg.peerDependencies || {};
|
|
175
|
+
|
|
176
|
+
// Check required peer dependencies
|
|
177
|
+
const requiredPeers = ['react', 'react-dom', 'react-router-dom'];
|
|
178
|
+
|
|
179
|
+
requiredPeers.forEach(dep => {
|
|
180
|
+
if (!allDeps[dep]) {
|
|
181
|
+
issues.push({
|
|
182
|
+
type: 'techStack',
|
|
183
|
+
file: 'package.json',
|
|
184
|
+
line: 1,
|
|
185
|
+
message: `Missing required dependency: ${dep}. Required version: ${peerDeps[dep] || 'latest'}`,
|
|
186
|
+
severity: 'error',
|
|
187
|
+
fix: `Install ${dep}: npm install ${dep}@${peerDeps[dep] || 'latest'}`,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Check React 19+ requirement
|
|
193
|
+
if (allDeps.react) {
|
|
194
|
+
const reactVersion = allDeps.react.replace(/[\^~]/, '');
|
|
195
|
+
const reactMajor = parseInt(reactVersion.split('.')[0]);
|
|
196
|
+
|
|
197
|
+
if (reactMajor < 19) {
|
|
198
|
+
issues.push({
|
|
199
|
+
type: 'techStack',
|
|
200
|
+
file: 'package.json',
|
|
201
|
+
line: 1,
|
|
202
|
+
message: `React version ${reactVersion} is below required version 19+. pace-core requires React 19+.`,
|
|
203
|
+
severity: 'error',
|
|
204
|
+
fix: 'Upgrade React: npm install react@^19.0.0 react-dom@^19.0.0',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return issues;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check Vite configuration (optimizeDeps, resolve.dedupe)
|
|
214
|
+
*/
|
|
215
|
+
function checkViteConfig(consumingAppPath) {
|
|
216
|
+
const issues = [];
|
|
217
|
+
|
|
218
|
+
const viteConfigFiles = findConfigFiles(consumingAppPath, ['vite.config.ts', 'vite.config.js']);
|
|
219
|
+
const viteConfigPath = viteConfigFiles['vite.config.ts'] || viteConfigFiles['vite.config.js'];
|
|
220
|
+
|
|
221
|
+
if (!viteConfigPath) {
|
|
222
|
+
return issues; // No vite config, skip
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const content = readFileSafe(viteConfigPath);
|
|
226
|
+
if (!content) {
|
|
227
|
+
return issues;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const relativePath = getRelativePath(viteConfigPath, consumingAppPath);
|
|
231
|
+
|
|
232
|
+
// Check for optimizeDeps.exclude (should exclude pace-core and react-router-dom)
|
|
233
|
+
const hasOptimizeDepsExclude = /optimizeDeps\s*:\s*\{[^}]*exclude/.test(content);
|
|
234
|
+
if (!hasOptimizeDepsExclude) {
|
|
235
|
+
issues.push({
|
|
236
|
+
type: 'viteConfig',
|
|
237
|
+
file: relativePath,
|
|
238
|
+
line: 1,
|
|
239
|
+
message: 'vite.config.ts missing optimizeDeps.exclude. Should exclude @jmruthers/pace-core and react-router-dom to prevent React context mismatches.',
|
|
240
|
+
severity: 'warning',
|
|
241
|
+
fix: 'Add: optimizeDeps: { exclude: [\'@jmruthers/pace-core\', \'react-router-dom\'] }',
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
// Check if pace-core is excluded
|
|
245
|
+
const excludesPaceCore = /exclude\s*:\s*\[[^\]]*['"]@jmruthers\/pace-core['"]/.test(content);
|
|
246
|
+
if (!excludesPaceCore) {
|
|
247
|
+
issues.push({
|
|
248
|
+
type: 'viteConfig',
|
|
249
|
+
file: relativePath,
|
|
250
|
+
line: 1,
|
|
251
|
+
message: 'vite.config.ts optimizeDeps.exclude should include @jmruthers/pace-core to prevent React context mismatches.',
|
|
252
|
+
severity: 'warning',
|
|
253
|
+
fix: 'Add \'@jmruthers/pace-core\' to optimizeDeps.exclude array',
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Check for resolve.dedupe (should dedupe React dependencies)
|
|
259
|
+
const hasResolveDedupe = /resolve\s*:\s*\{[^}]*dedupe/.test(content);
|
|
260
|
+
if (!hasResolveDedupe) {
|
|
261
|
+
issues.push({
|
|
262
|
+
type: 'viteConfig',
|
|
263
|
+
file: relativePath,
|
|
264
|
+
line: 1,
|
|
265
|
+
message: 'vite.config.ts missing resolve.dedupe. Should dedupe React dependencies to prevent context mismatches.',
|
|
266
|
+
severity: 'warning',
|
|
267
|
+
fix: 'Add: resolve: { dedupe: [\'react\', \'react-dom\', \'react-router-dom\'] }',
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return issues;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Run audit for Standard 7: API & Tech Stack
|
|
276
|
+
* @param {string} consumingAppPath - Path to consuming app
|
|
277
|
+
* @returns {object} - Audit results with issues array
|
|
278
|
+
*/
|
|
279
|
+
function runStandard7Audit(consumingAppPath) {
|
|
280
|
+
const issues = [];
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
issues.push(...checkRPCNaming(consumingAppPath));
|
|
284
|
+
issues.push(...checkTechStackVersions(consumingAppPath));
|
|
285
|
+
issues.push(...checkViteConfig(consumingAppPath));
|
|
286
|
+
} catch (error) {
|
|
287
|
+
return {
|
|
288
|
+
standard: '07-api-tech-stack',
|
|
289
|
+
issues: [],
|
|
290
|
+
error: error.message,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
standard: '07-api-tech-stack',
|
|
296
|
+
issues,
|
|
297
|
+
error: null,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
module.exports = { runStandard7Audit };
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard 8: Testing & Documentation Audit
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Audit/Standard8
|
|
5
|
+
*
|
|
6
|
+
* Audits consuming apps for compliance with Standard 8: Testing & Documentation.
|
|
7
|
+
* Validates test structure, test timeout configuration, and testing tools.
|
|
8
|
+
*
|
|
9
|
+
* Reference: packages/core/docs/standards/8-testing-documentation-standards.md
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { findConfigFiles, readFileSafe, getRelativePath, findSourceFiles, directoryExists } = require('../utils/file-utils.cjs');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check test timeout configuration
|
|
17
|
+
*/
|
|
18
|
+
function checkTestTimeoutConfig(consumingAppPath) {
|
|
19
|
+
const issues = [];
|
|
20
|
+
|
|
21
|
+
// Check vitest.config.ts
|
|
22
|
+
const vitestConfigFiles = findConfigFiles(consumingAppPath, ['vitest.config.ts', 'vitest.config.js']);
|
|
23
|
+
const vitestConfigPath = vitestConfigFiles['vitest.config.ts'] || vitestConfigFiles['vitest.config.js'];
|
|
24
|
+
|
|
25
|
+
// Also check package.json for test scripts
|
|
26
|
+
const packageJsonPath = path.join(consumingAppPath, 'package.json');
|
|
27
|
+
const packageJsonContent = readFileSafe(packageJsonPath);
|
|
28
|
+
|
|
29
|
+
let hasTimeoutInConfig = false;
|
|
30
|
+
let hasTimeoutInScripts = false;
|
|
31
|
+
|
|
32
|
+
// Check vitest.config.ts
|
|
33
|
+
if (vitestConfigPath) {
|
|
34
|
+
const content = readFileSafe(vitestConfigPath);
|
|
35
|
+
if (content) {
|
|
36
|
+
const hasTestTimeout = /testTimeout\s*[:=]/.test(content);
|
|
37
|
+
const hasHookTimeout = /hookTimeout\s*[:=]/.test(content);
|
|
38
|
+
|
|
39
|
+
if (hasTestTimeout || hasHookTimeout) {
|
|
40
|
+
hasTimeoutInConfig = true;
|
|
41
|
+
} else {
|
|
42
|
+
const relativePath = getRelativePath(vitestConfigPath, consumingAppPath);
|
|
43
|
+
issues.push({
|
|
44
|
+
type: 'testTimeout',
|
|
45
|
+
file: relativePath,
|
|
46
|
+
line: 1,
|
|
47
|
+
message: 'vitest.config.ts missing test timeout configuration. Tests may hang indefinitely without timeouts.',
|
|
48
|
+
severity: 'error',
|
|
49
|
+
fix: 'Add: test: { testTimeout: 10000, hookTimeout: 10000, teardownTimeout: 5000 }',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check package.json scripts
|
|
56
|
+
if (packageJsonContent) {
|
|
57
|
+
try {
|
|
58
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
59
|
+
const scripts = packageJson.scripts || {};
|
|
60
|
+
|
|
61
|
+
const testScripts = Object.values(scripts).filter(script =>
|
|
62
|
+
typeof script === 'string' && script.includes('vitest')
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
hasTimeoutInScripts = testScripts.some(script =>
|
|
66
|
+
script.includes('--test-timeout') || script.includes('testTimeout')
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (!hasTimeoutInConfig && !hasTimeoutInScripts && testScripts.length > 0) {
|
|
70
|
+
issues.push({
|
|
71
|
+
type: 'testTimeout',
|
|
72
|
+
file: 'package.json',
|
|
73
|
+
line: 1,
|
|
74
|
+
message: 'Test scripts missing timeout configuration. Tests may hang indefinitely without timeouts.',
|
|
75
|
+
severity: 'error',
|
|
76
|
+
fix: 'Add --test-timeout=10000 to test scripts or configure in vitest.config.ts',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
// Skip if package.json can't be parsed
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return issues;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check testing tools (React Testing Library, userEvent, Vitest)
|
|
89
|
+
*/
|
|
90
|
+
function checkTestingTools(consumingAppPath) {
|
|
91
|
+
const issues = [];
|
|
92
|
+
|
|
93
|
+
const packageJsonPath = path.join(consumingAppPath, 'package.json');
|
|
94
|
+
const packageJsonContent = readFileSafe(packageJsonPath);
|
|
95
|
+
|
|
96
|
+
if (!packageJsonContent) {
|
|
97
|
+
return issues;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let packageJson;
|
|
101
|
+
try {
|
|
102
|
+
packageJson = JSON.parse(packageJsonContent);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
return issues;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const allDeps = {
|
|
108
|
+
...(packageJson.dependencies || {}),
|
|
109
|
+
...(packageJson.devDependencies || {}),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Check for required testing tools
|
|
113
|
+
const requiredTools = [
|
|
114
|
+
{ name: 'vitest', description: 'Test runner' },
|
|
115
|
+
{ name: '@testing-library/react', description: 'React Testing Library' },
|
|
116
|
+
{ name: '@testing-library/user-event', description: 'userEvent for interaction simulation' },
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
requiredTools.forEach(({ name, description }) => {
|
|
120
|
+
if (!allDeps[name]) {
|
|
121
|
+
issues.push({
|
|
122
|
+
type: 'testingTools',
|
|
123
|
+
file: 'package.json',
|
|
124
|
+
line: 1,
|
|
125
|
+
message: `Missing required testing tool: ${name} (${description})`,
|
|
126
|
+
severity: 'warning',
|
|
127
|
+
fix: `Install: npm install --save-dev ${name}`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return issues;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Check test file structure (colocation, naming)
|
|
137
|
+
*/
|
|
138
|
+
function checkTestStructure(consumingAppPath) {
|
|
139
|
+
const issues = [];
|
|
140
|
+
|
|
141
|
+
const srcDir = path.join(consumingAppPath, 'src');
|
|
142
|
+
if (!directoryExists(srcDir)) {
|
|
143
|
+
return issues;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Find all test files
|
|
147
|
+
const testFiles = findSourceFiles(srcDir).filter(file =>
|
|
148
|
+
file.includes('.test.') || file.includes('.spec.')
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
testFiles.forEach(testFile => {
|
|
152
|
+
const content = readFileSafe(testFile);
|
|
153
|
+
if (!content) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const relativePath = getRelativePath(testFile, consumingAppPath);
|
|
158
|
+
|
|
159
|
+
// Check if using .spec instead of .test
|
|
160
|
+
if (testFile.includes('.spec.')) {
|
|
161
|
+
issues.push({
|
|
162
|
+
type: 'testStructure',
|
|
163
|
+
file: relativePath,
|
|
164
|
+
line: 1,
|
|
165
|
+
message: 'Test file uses .spec extension. Should use .test extension for consistency.',
|
|
166
|
+
severity: 'warning',
|
|
167
|
+
fix: `Rename to ${testFile.replace('.spec.', '.test.')}`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return issues;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Run audit for Standard 8: Testing & Documentation
|
|
177
|
+
* @param {string} consumingAppPath - Path to consuming app
|
|
178
|
+
* @returns {object} - Audit results with issues array
|
|
179
|
+
*/
|
|
180
|
+
function runStandard8Audit(consumingAppPath) {
|
|
181
|
+
const issues = [];
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
issues.push(...checkTestTimeoutConfig(consumingAppPath));
|
|
185
|
+
issues.push(...checkTestingTools(consumingAppPath));
|
|
186
|
+
issues.push(...checkTestStructure(consumingAppPath));
|
|
187
|
+
} catch (error) {
|
|
188
|
+
return {
|
|
189
|
+
standard: '08-testing-documentation',
|
|
190
|
+
issues: [],
|
|
191
|
+
error: error.message,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
standard: '08-testing-documentation',
|
|
197
|
+
issues,
|
|
198
|
+
error: null,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
module.exports = { runStandard8Audit };
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard 9: Operations Audit
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Audit/Standard9
|
|
5
|
+
*
|
|
6
|
+
* Audits consuming apps for compliance with Standard 9: Operations.
|
|
7
|
+
* Validates error handling patterns, CI/CD configuration, and performance patterns.
|
|
8
|
+
*
|
|
9
|
+
* Reference: packages/core/docs/standards/9-operations-standards.md
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const { findSourceFiles, readFileSafe, getRelativePath, directoryExists } = require('../utils/file-utils.cjs');
|
|
15
|
+
const { getLineNumber, getCodeSnippet, isInCommentOrString } = require('../utils/code-utils.cjs');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check error handling patterns (ApiResult usage)
|
|
19
|
+
*/
|
|
20
|
+
function checkErrorHandlingPatterns(consumingAppPath) {
|
|
21
|
+
const issues = [];
|
|
22
|
+
|
|
23
|
+
const srcDir = path.join(consumingAppPath, 'src');
|
|
24
|
+
if (!directoryExists(srcDir)) {
|
|
25
|
+
return issues;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sourceFiles = findSourceFiles(srcDir);
|
|
29
|
+
|
|
30
|
+
// Check for API functions that don't use ApiResult pattern
|
|
31
|
+
sourceFiles.forEach(filePath => {
|
|
32
|
+
const content = readFileSafe(filePath);
|
|
33
|
+
if (!content) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Skip test files
|
|
38
|
+
if (filePath.includes('.test.') || filePath.includes('.spec.')) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const relativePath = getRelativePath(filePath, consumingAppPath);
|
|
43
|
+
|
|
44
|
+
// Look for async functions that might be API functions
|
|
45
|
+
const asyncFunctionPattern = /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\([^)]*\)\s*:\s*Promise/gi;
|
|
46
|
+
const matches = [...content.matchAll(asyncFunctionPattern)];
|
|
47
|
+
|
|
48
|
+
matches.forEach(match => {
|
|
49
|
+
const functionName = match[1];
|
|
50
|
+
const functionIndex = match.index;
|
|
51
|
+
|
|
52
|
+
// Skip if it's a hook
|
|
53
|
+
if (functionName.startsWith('use')) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check if function uses ApiResult
|
|
58
|
+
const functionBody = content.substring(functionIndex, Math.min(functionIndex + 500, content.length));
|
|
59
|
+
const usesApiResult = /ApiResult|ok:\s*(true|false)/.test(functionBody);
|
|
60
|
+
|
|
61
|
+
if (!usesApiResult) {
|
|
62
|
+
// Check if it's likely an API function (returns Promise, might throw errors)
|
|
63
|
+
const returnsPromise = /:\s*Promise/.test(match[0]);
|
|
64
|
+
const hasErrorHandling = /catch|throw|error/i.test(functionBody);
|
|
65
|
+
|
|
66
|
+
if (returnsPromise && hasErrorHandling) {
|
|
67
|
+
issues.push({
|
|
68
|
+
type: 'errorHandling',
|
|
69
|
+
file: relativePath,
|
|
70
|
+
line: getLineNumber(content, functionIndex),
|
|
71
|
+
message: `API function '${functionName}' does not use ApiResult pattern. Consider using ApiResult<T> for consistent error handling.`,
|
|
72
|
+
code: getCodeSnippet(content, functionIndex),
|
|
73
|
+
severity: 'info',
|
|
74
|
+
fix: 'Use ApiResult<T> type: type ApiResult<T> = { ok: true; data: T } | { ok: false; error: ApiError }',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return issues;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check CI/CD configuration
|
|
86
|
+
*/
|
|
87
|
+
function checkCICDConfig(consumingAppPath) {
|
|
88
|
+
const issues = [];
|
|
89
|
+
|
|
90
|
+
// Check for .github/workflows directory
|
|
91
|
+
const workflowsDir = path.join(consumingAppPath, '.github', 'workflows');
|
|
92
|
+
|
|
93
|
+
if (!directoryExists(workflowsDir)) {
|
|
94
|
+
issues.push({
|
|
95
|
+
type: 'cicd',
|
|
96
|
+
file: '.github/workflows/ (not found)',
|
|
97
|
+
line: 0,
|
|
98
|
+
message: 'CI/CD workflows directory not found. Consider setting up GitHub Actions for automated testing and deployment.',
|
|
99
|
+
severity: 'info',
|
|
100
|
+
fix: 'Create .github/workflows/ directory and add CI/CD workflow files',
|
|
101
|
+
});
|
|
102
|
+
return issues;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check for common workflow files
|
|
106
|
+
const workflowFiles = fs.existsSync(workflowsDir)
|
|
107
|
+
? fs.readdirSync(workflowsDir).filter(file => file.endsWith('.yml') || file.endsWith('.yaml'))
|
|
108
|
+
: [];
|
|
109
|
+
|
|
110
|
+
if (workflowFiles.length === 0) {
|
|
111
|
+
issues.push({
|
|
112
|
+
type: 'cicd',
|
|
113
|
+
file: '.github/workflows/',
|
|
114
|
+
line: 0,
|
|
115
|
+
message: 'No CI/CD workflow files found. Consider setting up automated testing and deployment.',
|
|
116
|
+
severity: 'info',
|
|
117
|
+
fix: 'Create workflow files in .github/workflows/ (e.g., ci.yml, deploy.yml)',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return issues;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check for error boundary usage
|
|
126
|
+
*/
|
|
127
|
+
function checkErrorBoundaries(consumingAppPath) {
|
|
128
|
+
const issues = [];
|
|
129
|
+
|
|
130
|
+
const srcDir = path.join(consumingAppPath, 'src');
|
|
131
|
+
if (!directoryExists(srcDir)) {
|
|
132
|
+
return issues;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const sourceFiles = findSourceFiles(srcDir);
|
|
136
|
+
|
|
137
|
+
// Check if ErrorBoundary is used
|
|
138
|
+
let hasErrorBoundary = false;
|
|
139
|
+
sourceFiles.forEach(filePath => {
|
|
140
|
+
const content = readFileSafe(filePath);
|
|
141
|
+
if (!content) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (/ErrorBoundary|errorBoundary/i.test(content)) {
|
|
146
|
+
hasErrorBoundary = true;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Check main.tsx or App.tsx for ErrorBoundary
|
|
151
|
+
const mainFiles = [
|
|
152
|
+
path.join(consumingAppPath, 'src', 'main.tsx'),
|
|
153
|
+
path.join(consumingAppPath, 'src', 'App.tsx'),
|
|
154
|
+
path.join(consumingAppPath, 'App.tsx'),
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
let hasErrorBoundaryInApp = false;
|
|
158
|
+
mainFiles.forEach(filePath => {
|
|
159
|
+
if (fs.existsSync(filePath)) {
|
|
160
|
+
const content = readFileSafe(filePath);
|
|
161
|
+
if (content && /ErrorBoundary|errorBoundary/i.test(content)) {
|
|
162
|
+
hasErrorBoundaryInApp = true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
if (!hasErrorBoundaryInApp && hasErrorBoundary) {
|
|
168
|
+
issues.push({
|
|
169
|
+
type: 'errorBoundary',
|
|
170
|
+
file: 'src/main.tsx or src/App.tsx',
|
|
171
|
+
line: 1,
|
|
172
|
+
message: 'ErrorBoundary component exists but is not used in main.tsx or App.tsx. Should wrap the app root to catch React errors.',
|
|
173
|
+
severity: 'warning',
|
|
174
|
+
fix: 'Wrap app root with ErrorBoundary in main.tsx or App.tsx',
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return issues;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Run audit for Standard 9: Operations
|
|
183
|
+
* @param {string} consumingAppPath - Path to consuming app
|
|
184
|
+
* @returns {object} - Audit results with issues array
|
|
185
|
+
*/
|
|
186
|
+
function runStandard9Audit(consumingAppPath) {
|
|
187
|
+
const issues = [];
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
issues.push(...checkErrorHandlingPatterns(consumingAppPath));
|
|
191
|
+
issues.push(...checkCICDConfig(consumingAppPath));
|
|
192
|
+
issues.push(...checkErrorBoundaries(consumingAppPath));
|
|
193
|
+
} catch (error) {
|
|
194
|
+
return {
|
|
195
|
+
standard: '09-operations',
|
|
196
|
+
issues: [],
|
|
197
|
+
error: error.message,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
standard: '09-operations',
|
|
203
|
+
issues,
|
|
204
|
+
error: null,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
module.exports = { runStandard9Audit };
|