@jmruthers/pace-core 0.6.6 → 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/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +12 -13
- 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/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
- package/cursor-rules/02-project-structure.mdc +37 -5
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
- package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
- 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/DataTable-7PMH7XN7.js +15 -0
- package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +26 -16
- package/dist/{chunk-FENMYN2U.js → chunk-5X4QLXRG.js} +1 -3
- package/dist/{chunk-4T7OBVTU.js → chunk-6F3IILHI.js} +1 -1
- package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
- package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
- package/dist/{chunk-7TYHROIV.js → chunk-BM4CQ5P3.js} +50 -8
- package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
- package/dist/{chunk-OHIK3MIO.js → chunk-GHYHJTYV.js} +2 -2
- package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
- package/dist/{chunk-LAZMKTTF.js → chunk-JGWDVX64.js} +281 -347
- package/dist/{chunk-MAGBIDNS.js → chunk-L4XMVJKY.js} +2 -2
- package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
- package/dist/{chunk-ZS5VO5JB.js → chunk-Q7Q7V5NV.js} +406 -451
- package/dist/{chunk-3O3WHILE.js → chunk-VBCS3DUA.js} +236 -60
- package/dist/{chunk-BVP2BCJF.js → chunk-ZKAWKYT4.js} +8 -8
- package/dist/components.d.ts +5 -4
- package/dist/components.js +27 -32
- package/dist/eslint-rules/index.cjs +22 -9
- package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
- 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/{rbac.cjs → 06-security-rbac.cjs} +26 -10
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/hooks.d.ts +5 -5
- package/dist/hooks.js +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +18 -17
- package/dist/rbac/index.js +6 -6
- package/dist/theming/runtime.d.ts +14 -1
- package/dist/theming/runtime.js +1 -1
- package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
- package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +8 -8
- package/docs/README.md +1 -1
- package/docs/api/modules.md +47 -31
- package/docs/api-reference/components.md +18 -20
- package/docs/api-reference/hooks.md +80 -80
- 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/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/quick-reference.md +4 -4
- 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 +1 -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/README.md +5 -5
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- 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 -1
- 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/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +204 -185
- package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
- package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
- 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 +203 -104
- 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/eslint-config-pace-core.cjs +21 -10
- package/package.json +6 -5
- package/scripts/install-cursor-rules.cjs +11 -243
- package/scripts/install-eslint-config.cjs +284 -0
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +10 -10
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- 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/Calendar/Calendar.tsx +201 -47
- package/src/components/ContextSelector/ContextSelector.tsx +137 -153
- 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/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
- package/src/components/DataTable/components/EditableRow.tsx +5 -7
- 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/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/index.ts +2 -1
- package/src/components/DataTable/types.ts +0 -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 +7 -8
- package/src/components/Dialog/Dialog.test.tsx +1 -0
- package/src/components/Dialog/Dialog.tsx +25 -8
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileUpload/FileUpload.test.tsx +52 -14
- package/src/components/FileUpload/FileUpload.tsx +112 -130
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +86 -77
- package/src/components/Select/types.ts +3 -0
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
- package/src/hooks/public/usePublicEvent.ts +5 -5
- package/src/hooks/public/usePublicEventLogo.ts +5 -5
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +5 -5
- package/src/hooks/useAppConfig.ts +2 -2
- package/src/hooks/useEventTheme.test.ts +7 -7
- package/src/hooks/useEventTheme.ts +1 -4
- package/src/hooks/useFileDisplay.ts +2 -2
- 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/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
- 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/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/storage/README.md +1 -1
- package/cursor-rules/01-standards-compliance.mdc +0 -285
- package/cursor-rules/04-testing-standards.mdc +0 -270
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
- package/cursor-rules/06-code-quality.mdc +0 -311
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
- package/cursor-rules/10-error-handling-patterns.mdc +0 -179
- package/cursor-rules/11-performance-optimization.mdc +0 -169
- package/cursor-rules/12-ci-cd-integration.mdc +0 -150
- package/dist/DataTable-LRJL4IRV.js +0 -15
- package/dist/eslint-rules/rules/compliance.cjs +0 -348
- package/dist/eslint-rules/rules/components.cjs +0 -113
- package/dist/eslint-rules/rules/imports.cjs +0 -102
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -604
- 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-standards-compliance.md +0 -188
- package/docs/standards/03-solid-principles.md +0 -39
- package/docs/standards/04-testing-standards.md +0 -36
- package/docs/standards/05-bug-reports-and-features.md +0 -27
- package/docs/standards/06-code-quality.md +0 -34
- package/docs/standards/07-tech-stack-compliance.md +0 -30
- package/docs/standards/10-error-handling-patterns.md +0 -401
- package/docs/standards/11-performance-optimization.md +0 -348
- package/docs/standards/12-ci-cd-integration.md +0 -370
- package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
- package/scripts/audit/audit-compliance.cjs +0 -1295
- package/scripts/audit/audit-components.cjs +0 -260
- package/scripts/audit/audit-rbac.cjs +0 -954
- package/scripts/audit/audit-standards.cjs +0 -1268
- package/scripts/audit/index.cjs +0 -1927
- package/src/components/DataTable/components/DataTableBody.tsx +0 -478
- 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/index.cjs +0 -22
- package/src/eslint-rules/rules/components.cjs +0 -113
- package/src/eslint-rules/rules/imports.cjs +0 -102
- package/src/eslint-rules/rules/rbac.cjs +0 -790
- package/src/eslint-rules/utils/helpers.cjs +0 -42
- package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Comprehensive Audit Script for Consuming Apps
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Audit
|
|
7
|
+
*
|
|
8
|
+
* Audits consuming apps against pace-core standards and generates a markdown report.
|
|
9
|
+
* Organized by the 10-file standards structure (Standards 1-9).
|
|
10
|
+
*
|
|
11
|
+
* This is Layer 4 of the quality enforcement strategy:
|
|
12
|
+
* - Layer 1: Standards Documents (Source of Truth)
|
|
13
|
+
* - Layer 2: Cursor Rules (Real-time Guidance)
|
|
14
|
+
* - Layer 3: ESLint (Fast, Local Static Analysis)
|
|
15
|
+
* - Layer 4: Audit Tool (Deep, System-Level Analysis) ← You are here
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* node packages/core/audit-tool/index.cjs [path-to-consuming-app] [--output report.md]
|
|
19
|
+
* npm run audit:pace-core [path-to-consuming-app] [--output report.md]
|
|
20
|
+
*
|
|
21
|
+
* If no path provided, assumes current directory is consuming app.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
const path = require('path');
|
|
26
|
+
|
|
27
|
+
// Import dependency audit (runs before standards)
|
|
28
|
+
const { runDependencyAudit } = require('./00-dependencies.cjs');
|
|
29
|
+
|
|
30
|
+
// Import new standard-aligned audit modules
|
|
31
|
+
const { runStandard1Audit } = require('./audits/01-pace-core-compliance.cjs');
|
|
32
|
+
const { runStandard2Audit } = require('./audits/02-project-structure.cjs');
|
|
33
|
+
const { runStandard3Audit } = require('./audits/03-architecture.cjs');
|
|
34
|
+
const { runStandard4Audit } = require('./audits/04-code-quality.cjs');
|
|
35
|
+
const { runStandard5Audit } = require('./audits/05-styling.cjs');
|
|
36
|
+
const { runStandard6Audit } = require('./audits/06-security-rbac.cjs');
|
|
37
|
+
const { runStandard7Audit } = require('./audits/07-api-tech-stack.cjs');
|
|
38
|
+
const { runStandard8Audit } = require('./audits/08-testing-documentation.cjs');
|
|
39
|
+
const { runStandard9Audit } = require('./audits/09-operations.cjs');
|
|
40
|
+
|
|
41
|
+
// Import report utilities
|
|
42
|
+
const { generateMarkdownReport, generateSummary } = require('./utils/report-utils.cjs');
|
|
43
|
+
|
|
44
|
+
// Colors for terminal output
|
|
45
|
+
const colors = {
|
|
46
|
+
reset: '\x1b[0m',
|
|
47
|
+
red: '\x1b[31m',
|
|
48
|
+
green: '\x1b[32m',
|
|
49
|
+
yellow: '\x1b[33m',
|
|
50
|
+
blue: '\x1b[34m',
|
|
51
|
+
cyan: '\x1b[36m',
|
|
52
|
+
bold: '\x1b[1m',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Run all standard audits
|
|
57
|
+
*/
|
|
58
|
+
function runAllAudits(consumingAppPath, showProgress = false) {
|
|
59
|
+
const results = {};
|
|
60
|
+
|
|
61
|
+
const standardNames = {
|
|
62
|
+
'01-pace-core-compliance': 'pace-core Compliance',
|
|
63
|
+
'02-project-structure': 'Project Structure',
|
|
64
|
+
'03-architecture': 'Architecture',
|
|
65
|
+
'04-code-quality': 'Code Quality',
|
|
66
|
+
'05-styling': 'Styling',
|
|
67
|
+
'06-security-rbac': 'Security & RBAC',
|
|
68
|
+
'07-api-tech-stack': 'API & Tech Stack',
|
|
69
|
+
'08-testing-documentation': 'Testing & Documentation',
|
|
70
|
+
'09-operations': 'Operations',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const auditFunctions = [
|
|
74
|
+
{ key: '01-pace-core-compliance', name: standardNames['01-pace-core-compliance'], fn: runStandard1Audit },
|
|
75
|
+
{ key: '02-project-structure', name: standardNames['02-project-structure'], fn: runStandard2Audit },
|
|
76
|
+
{ key: '03-architecture', name: standardNames['03-architecture'], fn: runStandard3Audit },
|
|
77
|
+
{ key: '04-code-quality', name: standardNames['04-code-quality'], fn: runStandard4Audit },
|
|
78
|
+
{ key: '05-styling', name: standardNames['05-styling'], fn: runStandard5Audit },
|
|
79
|
+
{ key: '06-security-rbac', name: standardNames['06-security-rbac'], fn: runStandard6Audit },
|
|
80
|
+
{ key: '07-api-tech-stack', name: standardNames['07-api-tech-stack'], fn: runStandard7Audit },
|
|
81
|
+
{ key: '08-testing-documentation', name: standardNames['08-testing-documentation'], fn: runStandard8Audit },
|
|
82
|
+
{ key: '09-operations', name: standardNames['09-operations'], fn: runStandard9Audit },
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
auditFunctions.forEach(({ key, name, fn }) => {
|
|
86
|
+
if (showProgress) {
|
|
87
|
+
console.log(`${colors.blue}Running ${name} audit...${colors.reset}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const result = fn(consumingAppPath);
|
|
92
|
+
results[key] = result;
|
|
93
|
+
|
|
94
|
+
if (result.error && showProgress) {
|
|
95
|
+
console.warn(`${colors.yellow}Warning: ${name} audit failed: ${result.error}${colors.reset}`);
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (showProgress) {
|
|
99
|
+
console.warn(`${colors.yellow}Warning: ${name} audit encountered an error: ${error.message}${colors.reset}`);
|
|
100
|
+
}
|
|
101
|
+
results[key] = {
|
|
102
|
+
standard: key,
|
|
103
|
+
issues: [],
|
|
104
|
+
error: error.message,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return results;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Main function
|
|
114
|
+
*/
|
|
115
|
+
function main() {
|
|
116
|
+
const args = process.argv.slice(2);
|
|
117
|
+
const outputArg = args.find(arg => arg.startsWith('--output'));
|
|
118
|
+
const outputPath = outputArg ? (outputArg.includes('=') ? outputArg.split('=')[1] : args[args.indexOf(outputArg) + 1] || 'audit-report.md') : null;
|
|
119
|
+
const consumingAppPath = args.find(arg => !arg.startsWith('--') && arg !== outputPath) || process.cwd();
|
|
120
|
+
|
|
121
|
+
console.log(`${colors.bold}${colors.cyan}pace-core Comprehensive Audit${colors.reset}\n`);
|
|
122
|
+
console.log(`${colors.cyan}${'='.repeat(50)}${colors.reset}\n`);
|
|
123
|
+
|
|
124
|
+
// Get project info from package.json
|
|
125
|
+
const packageJsonPath = path.join(consumingAppPath, 'package.json');
|
|
126
|
+
let projectName = 'unknown';
|
|
127
|
+
let paceCoreVersion = 'unknown';
|
|
128
|
+
|
|
129
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
130
|
+
try {
|
|
131
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
132
|
+
projectName = packageJson.name || 'unknown';
|
|
133
|
+
|
|
134
|
+
// Find pace-core version
|
|
135
|
+
const allDeps = {
|
|
136
|
+
...(packageJson.dependencies || {}),
|
|
137
|
+
...(packageJson.devDependencies || {}),
|
|
138
|
+
};
|
|
139
|
+
paceCoreVersion = allDeps['@jmruthers/pace-core'] || 'not found in package.json';
|
|
140
|
+
} catch (error) {
|
|
141
|
+
// Skip if package.json can't be parsed
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(`Project: ${colors.bold}${projectName}${colors.reset}`);
|
|
146
|
+
console.log(`pace-core: ${colors.bold}${paceCoreVersion}${colors.reset}\n`);
|
|
147
|
+
|
|
148
|
+
// Run dependency audit
|
|
149
|
+
console.log(`${colors.blue}Running dependency audit...${colors.reset}`);
|
|
150
|
+
const dependencyResult = runDependencyAudit(consumingAppPath);
|
|
151
|
+
|
|
152
|
+
// Run all standard audits
|
|
153
|
+
const standardResults = runAllAudits(consumingAppPath, true);
|
|
154
|
+
|
|
155
|
+
// Combine results
|
|
156
|
+
const allResults = {
|
|
157
|
+
...standardResults,
|
|
158
|
+
dependencies: dependencyResult,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Generate summary
|
|
162
|
+
const summary = generateSummary(standardResults);
|
|
163
|
+
|
|
164
|
+
// Display audit results summary
|
|
165
|
+
console.log(`\n${colors.bold}Audit Results:${colors.reset}\n`);
|
|
166
|
+
|
|
167
|
+
// Display each standard
|
|
168
|
+
Object.entries(standardResults).forEach(([key, result]) => {
|
|
169
|
+
const standardNames = {
|
|
170
|
+
'01-pace-core-compliance': 'pace-core Compliance',
|
|
171
|
+
'02-project-structure': 'Project Structure',
|
|
172
|
+
'03-architecture': 'Architecture',
|
|
173
|
+
'04-code-quality': 'Code Quality',
|
|
174
|
+
'05-styling': 'Styling',
|
|
175
|
+
'06-security-rbac': 'Security & RBAC',
|
|
176
|
+
'07-api-tech-stack': 'API & Tech Stack',
|
|
177
|
+
'08-testing-documentation': 'Testing & Documentation',
|
|
178
|
+
'09-operations': 'Operations',
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const name = standardNames[key] || key;
|
|
182
|
+
const issues = Array.isArray(result.issues) ? result.issues : Object.values(result.issues || {}).flat();
|
|
183
|
+
const count = issues.length;
|
|
184
|
+
|
|
185
|
+
if (count === 0) {
|
|
186
|
+
console.log(` ${colors.green}✅ ${name}: 0 issues${colors.reset}`);
|
|
187
|
+
} else {
|
|
188
|
+
console.log(` ${colors.red}❌ ${name}: ${count} issue(s)${colors.reset}`);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Display dependency audit
|
|
193
|
+
if (dependencyResult.error) {
|
|
194
|
+
console.log(` ${colors.red}❌ Dependency Audit: Error - ${dependencyResult.error}${colors.reset}`);
|
|
195
|
+
} else {
|
|
196
|
+
const depIssues = dependencyResult.issues || {};
|
|
197
|
+
const depCount = (depIssues.includedDeps?.length || 0) +
|
|
198
|
+
(depIssues.missingRequired?.length || 0) +
|
|
199
|
+
(depIssues.versionIssues?.length || 0) +
|
|
200
|
+
(depIssues.wrongLocation?.length || 0);
|
|
201
|
+
|
|
202
|
+
if (depCount === 0) {
|
|
203
|
+
console.log(` ${colors.green}✅ Dependency Audit: 0 issues${colors.reset}`);
|
|
204
|
+
} else {
|
|
205
|
+
console.log(` ${colors.red}❌ Dependency Audit: ${depCount} issue(s)${colors.reset}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Total summary
|
|
210
|
+
const totalIssues = summary.total + (dependencyResult.error ? 0 :
|
|
211
|
+
((dependencyResult.issues?.includedDeps?.length || 0) +
|
|
212
|
+
(dependencyResult.issues?.missingRequired?.length || 0) +
|
|
213
|
+
(dependencyResult.issues?.versionIssues?.length || 0) +
|
|
214
|
+
(dependencyResult.issues?.wrongLocation?.length || 0)));
|
|
215
|
+
|
|
216
|
+
console.log(`\n${colors.bold}Total Issues: ${totalIssues === 0 ? colors.green + '0 (All checks passed!)' : colors.red + totalIssues}${colors.reset}\n`);
|
|
217
|
+
|
|
218
|
+
// Generate and save markdown report
|
|
219
|
+
const markdownReport = generateMarkdownReport(standardResults, consumingAppPath);
|
|
220
|
+
|
|
221
|
+
// Generate timestamp in yyyymmddHHMM format
|
|
222
|
+
const now = new Date();
|
|
223
|
+
const year = now.getFullYear();
|
|
224
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
225
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
226
|
+
const hours = String(now.getHours()).padStart(2, '0');
|
|
227
|
+
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
228
|
+
const timestamp = `${year}${month}${day}${hours}${minutes}`;
|
|
229
|
+
|
|
230
|
+
// Helper function to add timestamp to filename
|
|
231
|
+
function addTimestampToFilename(filePath) {
|
|
232
|
+
const dir = path.dirname(filePath);
|
|
233
|
+
const ext = path.extname(filePath);
|
|
234
|
+
const name = path.basename(filePath, ext);
|
|
235
|
+
return path.join(dir, `${name}-${timestamp}${ext}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Determine report path
|
|
239
|
+
let reportPath;
|
|
240
|
+
if (outputPath) {
|
|
241
|
+
reportPath = addTimestampToFilename(path.join(consumingAppPath, outputPath));
|
|
242
|
+
} else {
|
|
243
|
+
// Default: save to audit/ directory
|
|
244
|
+
const auditDir = path.join(consumingAppPath, 'audit');
|
|
245
|
+
if (!fs.existsSync(auditDir)) {
|
|
246
|
+
fs.mkdirSync(auditDir, { recursive: true });
|
|
247
|
+
}
|
|
248
|
+
reportPath = path.join(auditDir, `pace-core-audit-${timestamp}.md`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Save report
|
|
252
|
+
fs.writeFileSync(reportPath, markdownReport, 'utf8');
|
|
253
|
+
|
|
254
|
+
// Display report location
|
|
255
|
+
const relativeReportPath = path.relative(consumingAppPath, reportPath);
|
|
256
|
+
console.log(`${colors.bold}Report saved to:${colors.reset} ${colors.cyan}${relativeReportPath}${colors.reset}\n`);
|
|
257
|
+
|
|
258
|
+
// Exit with error code if issues found
|
|
259
|
+
process.exit(totalIssues > 0 ? 1 : 0);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Export for programmatic usage
|
|
263
|
+
module.exports = {
|
|
264
|
+
runAudit: main,
|
|
265
|
+
runAllAudits,
|
|
266
|
+
runStandardAudit: (standardNumber, consumingAppPath) => {
|
|
267
|
+
const auditFunctions = {
|
|
268
|
+
'1': runStandard1Audit,
|
|
269
|
+
'2': runStandard2Audit,
|
|
270
|
+
'3': runStandard3Audit,
|
|
271
|
+
'4': runStandard4Audit,
|
|
272
|
+
'5': runStandard5Audit,
|
|
273
|
+
'6': runStandard6Audit,
|
|
274
|
+
'7': runStandard7Audit,
|
|
275
|
+
'8': runStandard8Audit,
|
|
276
|
+
'9': runStandard9Audit,
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const fn = auditFunctions[standardNumber];
|
|
280
|
+
if (!fn) {
|
|
281
|
+
throw new Error(`Invalid standard number: ${standardNumber}. Must be 1-9.`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return fn(consumingAppPath);
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// Run if called directly
|
|
289
|
+
if (require.main === module) {
|
|
290
|
+
main();
|
|
291
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Analysis Utilities for Audit Tool
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Audit/utils/code-utils
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get line number from index in content
|
|
9
|
+
* @param {string} content - File content
|
|
10
|
+
* @param {number} index - Character index
|
|
11
|
+
* @returns {number} - Line number (1-indexed)
|
|
12
|
+
*/
|
|
13
|
+
function getLineNumber(content, index) {
|
|
14
|
+
return content.substring(0, index).split('\n').length;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get code snippet around a match for context
|
|
19
|
+
* @param {string} content - File content
|
|
20
|
+
* @param {number} index - Character index
|
|
21
|
+
* @param {number} before - Characters before index
|
|
22
|
+
* @param {number} after - Characters after index
|
|
23
|
+
* @returns {string} - Code snippet
|
|
24
|
+
*/
|
|
25
|
+
function getCodeSnippet(content, index, before = 30, after = 50) {
|
|
26
|
+
const start = Math.max(0, index - before);
|
|
27
|
+
const end = Math.min(content.length, index + after);
|
|
28
|
+
return content.substring(start, end).trim();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if content is in a comment or string (for TypeScript/JavaScript)
|
|
33
|
+
* @param {string} content - File content
|
|
34
|
+
* @param {number} index - Character index
|
|
35
|
+
* @returns {boolean} - True if in comment or string
|
|
36
|
+
*/
|
|
37
|
+
function isInCommentOrString(content, index) {
|
|
38
|
+
const before = content.substring(0, index);
|
|
39
|
+
|
|
40
|
+
// Check for line comments
|
|
41
|
+
const lastLineComment = before.lastIndexOf('//');
|
|
42
|
+
const lastNewline = before.lastIndexOf('\n');
|
|
43
|
+
if (lastLineComment > lastNewline) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check for block comments
|
|
48
|
+
const lastBlockCommentStart = before.lastIndexOf('/*');
|
|
49
|
+
const lastBlockCommentEnd = before.lastIndexOf('*/');
|
|
50
|
+
if (lastBlockCommentStart > lastBlockCommentEnd) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check for string literals (simple check)
|
|
55
|
+
const singleQuoteMatches = [...before.matchAll(/'/g)];
|
|
56
|
+
const doubleQuoteMatches = [...before.matchAll(/"/g)];
|
|
57
|
+
const backtickMatches = [...before.matchAll(/`/g)];
|
|
58
|
+
|
|
59
|
+
// Simple heuristic: if odd number of quotes before, might be in string
|
|
60
|
+
const inSingleQuote = singleQuoteMatches.length % 2 === 1;
|
|
61
|
+
const inDoubleQuote = doubleQuoteMatches.length % 2 === 1;
|
|
62
|
+
const inBacktick = backtickMatches.length % 2 === 1;
|
|
63
|
+
|
|
64
|
+
return inSingleQuote || inDoubleQuote || inBacktick;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Check if content is in a comment or string (for SQL)
|
|
69
|
+
* @param {string} content - SQL content
|
|
70
|
+
* @param {number} index - Character index
|
|
71
|
+
* @returns {boolean} - True if in comment or string
|
|
72
|
+
*/
|
|
73
|
+
function isInCommentOrStringSQL(content, index) {
|
|
74
|
+
const before = content.substring(0, index);
|
|
75
|
+
|
|
76
|
+
// Check for SQL line comments
|
|
77
|
+
const lastLineComment = before.lastIndexOf('--');
|
|
78
|
+
const lastNewline = before.lastIndexOf('\n');
|
|
79
|
+
if (lastLineComment > lastNewline && !before.substring(lastLineComment, index).includes('\n')) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check for block comments
|
|
84
|
+
const lastBlockCommentStart = before.lastIndexOf('/*');
|
|
85
|
+
const lastBlockCommentEnd = before.lastIndexOf('*/');
|
|
86
|
+
if (lastBlockCommentStart > lastBlockCommentEnd) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check for string literals (SQL uses single quotes)
|
|
91
|
+
const singleQuoteMatches = [...before.matchAll(/'/g)];
|
|
92
|
+
const inSingleQuote = singleQuoteMatches.length % 2 === 1;
|
|
93
|
+
|
|
94
|
+
return inSingleQuote;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Extract import statements from content
|
|
99
|
+
* @param {string} content - File content
|
|
100
|
+
* @returns {Array} - Array of import objects { source, specifiers, line }
|
|
101
|
+
*/
|
|
102
|
+
function parseImports(content) {
|
|
103
|
+
const imports = [];
|
|
104
|
+
const lines = content.split('\n');
|
|
105
|
+
|
|
106
|
+
lines.forEach((line, index) => {
|
|
107
|
+
// Match various import patterns
|
|
108
|
+
const importPatterns = [
|
|
109
|
+
/^import\s+.*\s+from\s+['"]([^'"]+)['"]/,
|
|
110
|
+
/^import\s+['"]([^'"]+)['"]/,
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
for (const pattern of importPatterns) {
|
|
114
|
+
const match = line.match(pattern);
|
|
115
|
+
if (match) {
|
|
116
|
+
const source = match[1];
|
|
117
|
+
const specifiers = [];
|
|
118
|
+
|
|
119
|
+
// Try to extract named imports
|
|
120
|
+
const namedMatch = line.match(/import\s+{([^}]+)}\s+from/);
|
|
121
|
+
if (namedMatch) {
|
|
122
|
+
namedMatch[1].split(',').forEach(spec => {
|
|
123
|
+
const trimmed = spec.trim();
|
|
124
|
+
if (trimmed) {
|
|
125
|
+
specifiers.push(trimmed);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check for default import
|
|
131
|
+
const defaultMatch = line.match(/import\s+(\w+)\s+from/);
|
|
132
|
+
if (defaultMatch && !line.includes('{')) {
|
|
133
|
+
specifiers.push('default');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
imports.push({
|
|
137
|
+
source,
|
|
138
|
+
specifiers,
|
|
139
|
+
line: index + 1,
|
|
140
|
+
fullLine: line.trim(),
|
|
141
|
+
});
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return imports;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Extract export statements from content
|
|
152
|
+
* @param {string} content - File content
|
|
153
|
+
* @returns {Array} - Array of export objects { name, type, line }
|
|
154
|
+
*/
|
|
155
|
+
function parseExports(content) {
|
|
156
|
+
const exports = [];
|
|
157
|
+
const lines = content.split('\n');
|
|
158
|
+
|
|
159
|
+
lines.forEach((line, index) => {
|
|
160
|
+
// Match export patterns
|
|
161
|
+
const exportPatterns = [
|
|
162
|
+
/^export\s+(?:function|const|class|interface|type)\s+(\w+)/,
|
|
163
|
+
/^export\s+default\s+(?:function\s+)?(\w+)?/,
|
|
164
|
+
/^export\s+{\s*(\w+)/,
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
for (const pattern of exportPatterns) {
|
|
168
|
+
const match = line.match(pattern);
|
|
169
|
+
if (match) {
|
|
170
|
+
const name = match[1] || 'default';
|
|
171
|
+
const type = line.includes('function') ? 'function' :
|
|
172
|
+
line.includes('const') ? 'const' :
|
|
173
|
+
line.includes('class') ? 'class' :
|
|
174
|
+
line.includes('interface') ? 'interface' :
|
|
175
|
+
line.includes('type') ? 'type' :
|
|
176
|
+
'other';
|
|
177
|
+
|
|
178
|
+
exports.push({
|
|
179
|
+
name,
|
|
180
|
+
type,
|
|
181
|
+
line: index + 1,
|
|
182
|
+
fullLine: line.trim(),
|
|
183
|
+
});
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
return exports;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Check if file imports from pace-core
|
|
194
|
+
* @param {string} content - File content
|
|
195
|
+
* @param {string} name - Component/hook/util name to check
|
|
196
|
+
* @returns {boolean} - True if imports from pace-core
|
|
197
|
+
*/
|
|
198
|
+
function importsFromPaceCore(content, name) {
|
|
199
|
+
const patterns = [
|
|
200
|
+
new RegExp(`import\\s+.*\\b${name}\\b.*from\\s+['"]@jmruthers/pace-core`),
|
|
201
|
+
new RegExp(`import\\s+.*\\b${name}\\b.*from\\s+['"]@jmruthers/pace-core/components`),
|
|
202
|
+
new RegExp(`import\\s+.*\\b${name}\\b.*from\\s+['"]@jmruthers/pace-core/hooks`),
|
|
203
|
+
new RegExp(`import\\s+.*\\b${name}\\b.*from\\s+['"]@jmruthers/pace-core/utils`),
|
|
204
|
+
new RegExp(`import\\s+.*\\b${name}\\b.*from\\s+['"]@jmruthers/pace-core/rbac`),
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
return patterns.some(pattern => pattern.test(content));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = {
|
|
211
|
+
getLineNumber,
|
|
212
|
+
getCodeSnippet,
|
|
213
|
+
isInCommentOrString,
|
|
214
|
+
isInCommentOrStringSQL,
|
|
215
|
+
parseImports,
|
|
216
|
+
parseExports,
|
|
217
|
+
importsFromPaceCore,
|
|
218
|
+
};
|