@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.
Files changed (246) hide show
  1. package/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +12 -13
  2. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  3. package/audit-tool/audits/02-project-structure.cjs +255 -0
  4. package/audit-tool/audits/03-architecture.cjs +196 -0
  5. package/audit-tool/audits/04-code-quality.cjs +149 -0
  6. package/audit-tool/audits/05-styling.cjs +224 -0
  7. package/audit-tool/audits/06-security-rbac.cjs +544 -0
  8. package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
  9. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  10. package/audit-tool/audits/09-operations.cjs +208 -0
  11. package/audit-tool/index.cjs +291 -0
  12. package/audit-tool/utils/code-utils.cjs +218 -0
  13. package/audit-tool/utils/file-utils.cjs +230 -0
  14. package/audit-tool/utils/report-utils.cjs +241 -0
  15. package/cursor-rules/00-standards-overview.mdc +156 -0
  16. package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
  17. package/cursor-rules/02-project-structure.mdc +37 -5
  18. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
  19. package/cursor-rules/04-code-quality.mdc +419 -0
  20. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
  21. package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
  22. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  23. package/cursor-rules/08-testing-documentation.mdc +324 -0
  24. package/cursor-rules/09-operations.mdc +365 -0
  25. package/dist/DataTable-7PMH7XN7.js +15 -0
  26. package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  27. package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +26 -16
  28. package/dist/{chunk-FENMYN2U.js → chunk-5X4QLXRG.js} +1 -3
  29. package/dist/{chunk-4T7OBVTU.js → chunk-6F3IILHI.js} +1 -1
  30. package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
  31. package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
  32. package/dist/{chunk-7TYHROIV.js → chunk-BM4CQ5P3.js} +50 -8
  33. package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
  34. package/dist/{chunk-OHIK3MIO.js → chunk-GHYHJTYV.js} +2 -2
  35. package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
  36. package/dist/{chunk-LAZMKTTF.js → chunk-JGWDVX64.js} +281 -347
  37. package/dist/{chunk-MAGBIDNS.js → chunk-L4XMVJKY.js} +2 -2
  38. package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
  39. package/dist/{chunk-ZS5VO5JB.js → chunk-Q7Q7V5NV.js} +406 -451
  40. package/dist/{chunk-3O3WHILE.js → chunk-VBCS3DUA.js} +236 -60
  41. package/dist/{chunk-BVP2BCJF.js → chunk-ZKAWKYT4.js} +8 -8
  42. package/dist/components.d.ts +5 -4
  43. package/dist/components.js +27 -32
  44. package/dist/eslint-rules/index.cjs +22 -9
  45. package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
  46. package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
  47. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  48. package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +26 -10
  49. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
  50. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  51. package/dist/hooks.d.ts +5 -5
  52. package/dist/hooks.js +6 -6
  53. package/dist/index.d.ts +6 -6
  54. package/dist/index.js +18 -17
  55. package/dist/rbac/index.js +6 -6
  56. package/dist/theming/runtime.d.ts +14 -1
  57. package/dist/theming/runtime.js +1 -1
  58. package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
  59. package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
  60. package/dist/utils.d.ts +2 -2
  61. package/dist/utils.js +8 -8
  62. package/docs/README.md +1 -1
  63. package/docs/api/modules.md +47 -31
  64. package/docs/api-reference/components.md +18 -20
  65. package/docs/api-reference/hooks.md +80 -80
  66. package/docs/api-reference/types.md +1 -1
  67. package/docs/api-reference/utilities.md +1 -1
  68. package/docs/architecture/README.md +1 -1
  69. package/docs/core-concepts/events.md +3 -3
  70. package/docs/core-concepts/organisations.md +6 -6
  71. package/docs/core-concepts/permissions.md +6 -6
  72. package/docs/documentation-index.md +12 -18
  73. package/docs/getting-started/documentation-index.md +1 -1
  74. package/docs/getting-started/examples/README.md +4 -4
  75. package/docs/getting-started/examples/full-featured-app.md +1 -1
  76. package/docs/getting-started/faq.md +2 -2
  77. package/docs/getting-started/quick-reference.md +4 -4
  78. package/docs/implementation-guides/authentication.md +15 -15
  79. package/docs/implementation-guides/component-styling.md +1 -1
  80. package/docs/implementation-guides/data-tables.md +126 -33
  81. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  82. package/docs/implementation-guides/dynamic-colors.md +3 -3
  83. package/docs/implementation-guides/file-upload-storage.md +2 -2
  84. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  85. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  86. package/docs/implementation-guides/large-datasets.md +3 -2
  87. package/docs/implementation-guides/organisation-security.md +2 -2
  88. package/docs/implementation-guides/performance.md +2 -2
  89. package/docs/implementation-guides/permission-enforcement.md +1 -1
  90. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  91. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  92. package/docs/rbac/README.md +5 -5
  93. package/docs/rbac/advanced-patterns.md +6 -6
  94. package/docs/rbac/api-reference.md +20 -20
  95. package/docs/rbac/event-based-apps.md +3 -3
  96. package/docs/rbac/examples.md +41 -41
  97. package/docs/rbac/getting-started.md +37 -37
  98. package/docs/rbac/performance.md +1 -1
  99. package/docs/rbac/quick-start.md +52 -52
  100. package/docs/rbac/secure-client-protection.md +1 -1
  101. package/docs/rbac/troubleshooting.md +1 -1
  102. package/docs/security/README.md +5 -5
  103. package/docs/standards/0-standards-overview.md +220 -0
  104. package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +204 -185
  105. package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
  106. package/docs/standards/3-architecture-standards.md +606 -0
  107. package/docs/standards/4-code-quality-standards.md +728 -0
  108. package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
  109. package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
  110. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  111. package/docs/standards/8-testing-documentation-standards.md +401 -0
  112. package/docs/standards/9-operations-standards.md +1102 -0
  113. package/docs/standards/README.md +203 -104
  114. package/docs/troubleshooting/README.md +4 -4
  115. package/docs/troubleshooting/common-issues.md +2 -2
  116. package/docs/troubleshooting/debugging.md +9 -9
  117. package/docs/troubleshooting/migration.md +4 -4
  118. package/eslint-config-pace-core.cjs +21 -10
  119. package/package.json +6 -5
  120. package/scripts/install-cursor-rules.cjs +11 -243
  121. package/scripts/install-eslint-config.cjs +284 -0
  122. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
  123. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  124. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +10 -10
  125. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  126. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  127. package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
  128. package/src/__tests__/templates/component.test.template.tsx +18 -15
  129. package/src/components/Calendar/Calendar.tsx +201 -47
  130. package/src/components/ContextSelector/ContextSelector.tsx +137 -153
  131. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  132. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
  133. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
  134. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  135. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  136. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  137. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  138. package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
  139. package/src/components/DataTable/components/EditableRow.tsx +5 -7
  140. package/src/components/DataTable/components/EmptyState.tsx +10 -9
  141. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  142. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  143. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  144. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  145. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  146. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  147. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  148. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  149. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  150. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
  151. package/src/components/DataTable/components/index.ts +2 -1
  152. package/src/components/DataTable/types.ts +0 -18
  153. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  154. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
  155. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  156. package/src/components/DateTimeField/DateTimeField.tsx +7 -8
  157. package/src/components/Dialog/Dialog.test.tsx +1 -0
  158. package/src/components/Dialog/Dialog.tsx +25 -8
  159. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  160. package/src/components/FileUpload/FileUpload.test.tsx +52 -14
  161. package/src/components/FileUpload/FileUpload.tsx +112 -130
  162. package/src/components/Progress/Progress.tsx +2 -4
  163. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  164. package/src/components/Select/Select.tsx +86 -77
  165. package/src/components/Select/types.ts +3 -0
  166. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  167. package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
  168. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
  169. package/src/hooks/public/usePublicEvent.ts +5 -5
  170. package/src/hooks/public/usePublicEventLogo.ts +5 -5
  171. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  172. package/src/hooks/public/usePublicRouteParams.ts +5 -5
  173. package/src/hooks/useAppConfig.ts +2 -2
  174. package/src/hooks/useEventTheme.test.ts +7 -7
  175. package/src/hooks/useEventTheme.ts +1 -4
  176. package/src/hooks/useFileDisplay.ts +2 -2
  177. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  178. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  179. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  180. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  181. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  182. package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
  183. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  184. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  185. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
  186. package/src/styles/core.css +7 -0
  187. package/src/theming/__tests__/parseEventColours.test.ts +9 -3
  188. package/src/theming/parseEventColours.ts +22 -10
  189. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  190. package/src/utils/storage/README.md +1 -1
  191. package/cursor-rules/01-standards-compliance.mdc +0 -285
  192. package/cursor-rules/04-testing-standards.mdc +0 -270
  193. package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
  194. package/cursor-rules/06-code-quality.mdc +0 -311
  195. package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
  196. package/cursor-rules/10-error-handling-patterns.mdc +0 -179
  197. package/cursor-rules/11-performance-optimization.mdc +0 -169
  198. package/cursor-rules/12-ci-cd-integration.mdc +0 -150
  199. package/dist/DataTable-LRJL4IRV.js +0 -15
  200. package/dist/eslint-rules/rules/compliance.cjs +0 -348
  201. package/dist/eslint-rules/rules/components.cjs +0 -113
  202. package/dist/eslint-rules/rules/imports.cjs +0 -102
  203. package/docs/best-practices/README.md +0 -472
  204. package/docs/best-practices/accessibility.md +0 -604
  205. package/docs/best-practices/common-patterns.md +0 -516
  206. package/docs/best-practices/deployment.md +0 -1103
  207. package/docs/best-practices/performance.md +0 -1328
  208. package/docs/best-practices/security.md +0 -940
  209. package/docs/best-practices/testing.md +0 -1034
  210. package/docs/rbac/compliance/compliance-guide.md +0 -544
  211. package/docs/standards/01-standards-compliance.md +0 -188
  212. package/docs/standards/03-solid-principles.md +0 -39
  213. package/docs/standards/04-testing-standards.md +0 -36
  214. package/docs/standards/05-bug-reports-and-features.md +0 -27
  215. package/docs/standards/06-code-quality.md +0 -34
  216. package/docs/standards/07-tech-stack-compliance.md +0 -30
  217. package/docs/standards/10-error-handling-patterns.md +0 -401
  218. package/docs/standards/11-performance-optimization.md +0 -348
  219. package/docs/standards/12-ci-cd-integration.md +0 -370
  220. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
  221. package/scripts/audit/audit-compliance.cjs +0 -1295
  222. package/scripts/audit/audit-components.cjs +0 -260
  223. package/scripts/audit/audit-rbac.cjs +0 -954
  224. package/scripts/audit/audit-standards.cjs +0 -1268
  225. package/scripts/audit/index.cjs +0 -1927
  226. package/src/components/DataTable/components/DataTableBody.tsx +0 -478
  227. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  228. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  229. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  230. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  231. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  232. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  233. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  234. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  235. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  236. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  237. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  238. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  239. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  240. package/src/components/DataTable/utils/debugTools.ts +0 -514
  241. package/src/eslint-rules/index.cjs +0 -22
  242. package/src/eslint-rules/rules/components.cjs +0 -113
  243. package/src/eslint-rules/rules/imports.cjs +0 -102
  244. package/src/eslint-rules/rules/rbac.cjs +0 -790
  245. package/src/eslint-rules/utils/helpers.cjs +0 -42
  246. 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
+ };