@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
@@ -1,1927 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Comprehensive Audit Script for Consuming Apps
5
- *
6
- * Audits consuming apps against pace-core standards and generates a markdown report.
7
- * Includes:
8
- * - Dependency compliance checks
9
- * - Code compliance checks (future)
10
- * - Markdown report generation
11
- *
12
- * Usage:
13
- * node scripts/audit/index.cjs [path-to-consuming-app] [--output report.md]
14
- *
15
- * If no path provided, assumes current directory is consuming app.
16
- */
17
-
18
- const fs = require('fs');
19
- const path = require('path');
20
-
21
- // Import audit functions
22
- const { runDependencyAudit } = require('./audit-dependencies.cjs');
23
- const { runComponentAudit } = require('./audit-components.cjs');
24
- const { runComplianceAudit } = require('./audit-compliance.cjs');
25
- const { runStandardsAudit } = require('./audit-standards.cjs');
26
- const { runRBACAudit } = require('./audit-rbac.cjs');
27
-
28
- // Colors for terminal output
29
- const colors = {
30
- reset: '\x1b[0m',
31
- red: '\x1b[31m',
32
- green: '\x1b[32m',
33
- yellow: '\x1b[33m',
34
- blue: '\x1b[34m',
35
- cyan: '\x1b[36m',
36
- bold: '\x1b[1m',
37
- };
38
-
39
- // Get pace-core package.json (for Vite alias checks)
40
- function findPaceCorePackageJson(consumingAppPath) {
41
- // Try relative to script location first (when in pace-core repo or installed package)
42
- let paceCorePath = path.resolve(__dirname, '../../package.json');
43
- if (fs.existsSync(paceCorePath)) {
44
- return paceCorePath;
45
- }
46
-
47
- // Try alternative location
48
- paceCorePath = path.resolve(__dirname, '../../../package.json');
49
- if (fs.existsSync(paceCorePath)) {
50
- return paceCorePath;
51
- }
52
-
53
- // Try finding from consuming app's node_modules
54
- const nodeModulesPath = path.join(consumingAppPath, 'node_modules', '@jmruthers', 'pace-core', 'package.json');
55
- if (fs.existsSync(nodeModulesPath)) {
56
- return nodeModulesPath;
57
- }
58
-
59
- return null;
60
- }
61
-
62
- // Run dependency audit and collect results (with additional checks)
63
- function runDependencyAuditWithExtras(consumingAppPath, showProgress = false) {
64
- // Get the base dependency audit results
65
- const baseResult = runDependencyAudit(consumingAppPath);
66
-
67
- if (baseResult.error) {
68
- return baseResult;
69
- }
70
-
71
- // Add Vite alias checks
72
- const paceCorePath = findPaceCorePackageJson(consumingAppPath);
73
- if (paceCorePath) {
74
- const paceCorePkg = JSON.parse(fs.readFileSync(paceCorePath, 'utf8'));
75
- const INCLUDED_DEPS = Object.keys(paceCorePkg.dependencies || {});
76
- const viteAliasIssues = checkViteAliases(consumingAppPath, INCLUDED_DEPS);
77
- baseResult.issues.viteAliases = viteAliasIssues;
78
- } else {
79
- baseResult.issues.viteAliases = [];
80
- }
81
-
82
- // Add component usage checks
83
- if (showProgress) {
84
- console.log(`${colors.blue}Running component usage audit...${colors.reset}`);
85
- }
86
- const componentAuditResult = runComponentAudit(consumingAppPath);
87
- if (componentAuditResult.error) {
88
- // If component audit fails, just log warning and continue
89
- if (showProgress) {
90
- console.warn(`${colors.yellow}Warning: Component audit failed: ${componentAuditResult.error}${colors.reset}`);
91
- }
92
- baseResult.issues.formIssues = [];
93
- } else {
94
- baseResult.issues.formIssues = componentAuditResult.issues.formIssues || [];
95
- }
96
-
97
- // Add compliance checks
98
- if (showProgress) {
99
- console.log(`${colors.blue}Running compliance audit...${colors.reset}`);
100
- }
101
- const complianceAuditResult = runComplianceAudit(consumingAppPath);
102
- if (complianceAuditResult.error) {
103
- // If compliance audit fails, just log warning and continue
104
- if (showProgress) {
105
- console.warn(`${colors.yellow}Warning: Compliance audit failed: ${complianceAuditResult.error}${colors.reset}`);
106
- }
107
- baseResult.issues.complianceIssues = {
108
- nativeElements: [],
109
- restrictedImports: [],
110
- customHooks: [],
111
- customUtils: [],
112
- supabaseClient: [],
113
- rbacSetup: [],
114
- rbacPermission: [],
115
- providers: [],
116
- coreStyles: [],
117
- inlineStyles: [],
118
- };
119
- } else {
120
- baseResult.issues.complianceIssues = complianceAuditResult.issues || {
121
- nativeElements: [],
122
- restrictedImports: [],
123
- customHooks: [],
124
- customUtils: [],
125
- supabaseClient: [],
126
- rbacSetup: [],
127
- rbacPermission: [],
128
- providers: [],
129
- coreStyles: [],
130
- inlineStyles: [],
131
- };
132
- }
133
-
134
- // Add standards checks
135
- if (showProgress) {
136
- console.log(`${colors.blue}Running standards audit...${colors.reset}`);
137
- }
138
- try {
139
- const standardsAuditResult = runStandardsAudit(consumingAppPath, showProgress);
140
- if (standardsAuditResult.error) {
141
- // If standards audit fails, just log warning and continue
142
- if (showProgress) {
143
- console.warn(`\n${colors.yellow}Warning: Standards audit failed: ${standardsAuditResult.error}${colors.reset}`);
144
- }
145
- baseResult.issues.standardsIssues = {
146
- cursorRuleset: [],
147
- typescriptConfig: [],
148
- anyTypes: [],
149
- namingConvention: [],
150
- rlsPolicy: [],
151
- rpcNaming: [],
152
- testingConfig: [],
153
- inputValidation: [],
154
- loggingSecurity: [],
155
- errorMessages: [],
156
- };
157
- } else {
158
- baseResult.issues.standardsIssues = standardsAuditResult.issues || {
159
- cursorRuleset: [],
160
- typescriptConfig: [],
161
- anyTypes: [],
162
- namingConvention: [],
163
- rlsPolicy: [],
164
- rpcNaming: [],
165
- testingConfig: [],
166
- inputValidation: [],
167
- loggingSecurity: [],
168
- errorMessages: [],
169
- };
170
- }
171
- } catch (error) {
172
- // Catch any unexpected errors
173
- if (showProgress) {
174
- console.warn(`\n${colors.yellow}Warning: Standards audit encountered an error: ${error.message}${colors.reset}`);
175
- console.warn(`${colors.yellow}Stack: ${error.stack}${colors.reset}`);
176
- }
177
- baseResult.issues.standardsIssues = {
178
- cursorRuleset: [],
179
- typescriptConfig: [],
180
- anyTypes: [],
181
- namingConvention: [],
182
- rlsPolicy: [],
183
- rpcNaming: [],
184
- testingConfig: [],
185
- inputValidation: [],
186
- loggingSecurity: [],
187
- errorMessages: [],
188
- };
189
- }
190
-
191
- // Add RBAC audit checks
192
- if (showProgress) {
193
- console.log(`${colors.blue}Running RBAC audit...${colors.reset}`);
194
- }
195
- try {
196
- const rbacAuditResult = runRBACAudit(consumingAppPath);
197
- if (rbacAuditResult.error) {
198
- // If RBAC audit fails, just log warning and continue
199
- if (showProgress) {
200
- console.warn(`\n${colors.yellow}Warning: RBAC audit failed: ${rbacAuditResult.error}${colors.reset}`);
201
- }
202
- baseResult.issues.rbacIssues = {
203
- rbacPageGuard: [],
204
- rbacWrapperComponent: [],
205
- rbacWrapperFunction: [],
206
- rbacResourceNames: [],
207
- rbacAccessDenied: [],
208
- rbacDirectRPC: [],
209
- rbacDirectTable: [],
210
- rbacHardcodedRole: [],
211
- rbacEnforcePermissions: [],
212
- rbacEdgeFunction: [],
213
- };
214
- } else {
215
- baseResult.issues.rbacIssues = rbacAuditResult.issues || {
216
- rbacPageGuard: [],
217
- rbacWrapperComponent: [],
218
- rbacWrapperFunction: [],
219
- rbacResourceNames: [],
220
- rbacAccessDenied: [],
221
- rbacDirectRPC: [],
222
- rbacDirectTable: [],
223
- rbacHardcodedRole: [],
224
- rbacEnforcePermissions: [],
225
- rbacEdgeFunction: [],
226
- };
227
- }
228
- } catch (error) {
229
- // Catch any unexpected errors
230
- if (showProgress) {
231
- console.warn(`\n${colors.yellow}Warning: RBAC audit encountered an error: ${error.message}${colors.reset}`);
232
- console.warn(`${colors.yellow}Stack: ${error.stack}${colors.reset}`);
233
- }
234
- baseResult.issues.rbacIssues = {
235
- rbacPageGuard: [],
236
- rbacWrapperComponent: [],
237
- rbacWrapperFunction: [],
238
- rbacResourceNames: [],
239
- rbacAccessDenied: [],
240
- rbacDirectRPC: [],
241
- rbacDirectTable: [],
242
- rbacHardcodedRole: [],
243
- rbacEnforcePermissions: [],
244
- rbacEdgeFunction: [],
245
- };
246
- }
247
-
248
- return baseResult;
249
- }
250
-
251
- // Check Vite config for problematic aliases
252
- function checkViteAliases(consumingAppPath, includedDeps) {
253
- const issues = [];
254
-
255
- // Find vite config file
256
- const viteConfigPaths = [
257
- path.join(consumingAppPath, 'vite.config.ts'),
258
- path.join(consumingAppPath, 'vite.config.js'),
259
- path.join(consumingAppPath, 'vite.config.mjs'),
260
- path.join(consumingAppPath, 'vite.config.cjs'),
261
- ];
262
-
263
- let viteConfigPath = null;
264
- for (const configPath of viteConfigPaths) {
265
- if (fs.existsSync(configPath)) {
266
- viteConfigPath = configPath;
267
- break;
268
- }
269
- }
270
-
271
- if (!viteConfigPath) {
272
- return issues; // No vite config found, skip check
273
- }
274
-
275
- try {
276
- const configContent = fs.readFileSync(viteConfigPath, 'utf8');
277
-
278
- // Check for problematic aliases that bypass pace-core exports
279
- // Pattern: "@jmruthers/pace-core/icons": "lucide-react" or similar
280
- const paceCoreAliasPattern = /['"]@jmruthers\/pace-core\/(icons|utils|components|hooks|rbac)['"]\s*:\s*['"]([^'"]+)['"]/g;
281
- let match;
282
-
283
- while ((match = paceCoreAliasPattern.exec(configContent)) !== null) {
284
- const paceCoreExport = match[1];
285
- const aliasTarget = match[2];
286
-
287
- // Check if alias targets an included dependency
288
- if (includedDeps.includes(aliasTarget)) {
289
- issues.push({
290
- alias: `@jmruthers/pace-core/${paceCoreExport}`,
291
- target: aliasTarget,
292
- file: path.basename(viteConfigPath),
293
- line: getLineNumber(configContent, match.index),
294
- });
295
- }
296
- }
297
-
298
- } catch (error) {
299
- // If we can't parse the config, that's okay - just skip this check
300
- // Could be TypeScript, complex config, etc.
301
- }
302
-
303
- return issues;
304
- }
305
-
306
- // Get line number from index in content
307
- function getLineNumber(content, index) {
308
- return content.substring(0, index).split('\n').length;
309
- }
310
-
311
- // Generate markdown report
312
- function generateMarkdownReport(auditResults) {
313
- const { projectName, paceCoreVersion, issues } = auditResults;
314
- const timestamp = new Date().toISOString();
315
-
316
- // Calculate issue counts
317
- const dependencyIssuesCount = issues.includedDeps.length +
318
- issues.missingRequired.length +
319
- issues.versionIssues.length +
320
- issues.wrongLocation.length +
321
- issues.viteAliases.length;
322
-
323
- const componentIssuesCount = issues.formIssues?.length || 0;
324
-
325
- const complianceIssues = issues.complianceIssues || {
326
- nativeElements: [],
327
- restrictedImports: [],
328
- customHooks: [],
329
- customUtils: [],
330
- supabaseClient: [],
331
- rbacSetup: [],
332
- providers: [],
333
- coreStyles: [],
334
- inlineStyles: [],
335
- };
336
-
337
- const complianceIssuesCount = Object.values(complianceIssues).reduce((sum, arr) => sum + arr.length, 0);
338
-
339
- const standardsIssues = issues.standardsIssues || {
340
- cursorRuleset: [],
341
- typescriptConfig: [],
342
- anyTypes: [],
343
- namingConvention: [],
344
- rlsPolicy: [],
345
- rpcNaming: [],
346
- testingConfig: [],
347
- inputValidation: [],
348
- loggingSecurity: [],
349
- errorMessages: [],
350
- };
351
-
352
- const standardsIssuesCount = Object.values(standardsIssues).reduce((sum, arr) => sum + arr.length, 0);
353
-
354
- const rbacIssues = issues.rbacIssues || {
355
- rbacPageGuard: [],
356
- rbacWrapperComponent: [],
357
- rbacWrapperFunction: [],
358
- rbacResourceNames: [],
359
- rbacAccessDenied: [],
360
- rbacDirectRPC: [],
361
- rbacDirectTable: [],
362
- rbacHardcodedRole: [],
363
- rbacEnforcePermissions: [],
364
- rbacEdgeFunction: [],
365
- };
366
-
367
- const rbacIssuesCount = Object.values(rbacIssues).reduce((sum, arr) => sum + arr.length, 0);
368
-
369
- const totalIssues = dependencyIssuesCount + componentIssuesCount + complianceIssuesCount + standardsIssuesCount + rbacIssuesCount;
370
-
371
- let report = `# pace-core Audit Report\n\n`;
372
- report += `**Project:** ${projectName}\n`;
373
- report += `**pace-core Version:** ${paceCoreVersion}\n`;
374
- report += `**Generated:** ${timestamp}\n\n`;
375
- report += `---\n\n`;
376
-
377
- // Summary Section (matches terminal output)
378
- report += `## 📊 Summary\n\n`;
379
-
380
- if (totalIssues === 0 && issues.missingOptional.length === 0) {
381
- report += `✅ **All Checks Passed**\n\n`;
382
- report += `- ✅ Dependency Audit: 0 issues\n`;
383
- report += `- ✅ Component Usage Audit: 0 issues\n`;
384
- report += `- ✅ Compliance Audit: 0 issues\n`;
385
- report += `- ✅ Standards Audit: 0 issues\n`;
386
- report += `- ✅ RBAC Audit: 0 issues\n`;
387
- report += `- ✅ **Total Issues: 0**\n\n`;
388
- } else {
389
- report += `⚠️ **Issues Found**\n\n`;
390
-
391
- // Dependency Audit Summary
392
- if (dependencyIssuesCount === 0) {
393
- report += `- ✅ Dependency Audit: 0 issues\n`;
394
- } else {
395
- report += `- ❌ Dependency Audit: ${dependencyIssuesCount} issue(s)\n`;
396
- if (issues.includedDeps.length > 0) {
397
- report += ` - Included Dependencies: ${issues.includedDeps.length}\n`;
398
- }
399
- if (issues.missingRequired.length > 0) {
400
- report += ` - Missing Required: ${issues.missingRequired.length}\n`;
401
- }
402
- if (issues.versionIssues.length > 0) {
403
- report += ` - Version Issues: ${issues.versionIssues.length}\n`;
404
- }
405
- if (issues.wrongLocation.length > 0) {
406
- report += ` - Wrong Location: ${issues.wrongLocation.length}\n`;
407
- }
408
- if (issues.viteAliases.length > 0) {
409
- report += ` - Vite Aliases: ${issues.viteAliases.length}\n`;
410
- }
411
- }
412
-
413
- // Component Usage Audit Summary
414
- if (componentIssuesCount === 0) {
415
- report += `- ✅ Component Usage Audit: 0 issues\n`;
416
- } else {
417
- const plainFormTagsCount = issues.formIssues.filter(i => i.type === 'plainFormTag').length;
418
- const reactHookFormImportsCount = issues.formIssues.filter(i => i.type === 'reactHookFormImport').length;
419
- report += `- ❌ Component Usage Audit: ${componentIssuesCount} issue(s)\n`;
420
- if (plainFormTagsCount > 0) {
421
- report += ` - Plain <form> tags: ${plainFormTagsCount}\n`;
422
- }
423
- if (reactHookFormImportsCount > 0) {
424
- report += ` - Direct react-hook-form imports: ${reactHookFormImportsCount}\n`;
425
- }
426
- }
427
-
428
- // Compliance Audit Summary
429
- if (complianceIssuesCount === 0) {
430
- report += `- ✅ Compliance Audit: 0 issues\n`;
431
- } else {
432
- report += `- ❌ Compliance Audit: ${complianceIssuesCount} issue(s)\n`;
433
- if (complianceIssues.nativeElements.length > 0) {
434
- report += ` - Native HTML elements: ${complianceIssues.nativeElements.length}\n`;
435
- }
436
- if (complianceIssues.restrictedImports.length > 0) {
437
- report += ` - Restricted imports: ${complianceIssues.restrictedImports.length}\n`;
438
- }
439
- if (complianceIssues.customHooks.length > 0) {
440
- report += ` - Custom hooks: ${complianceIssues.customHooks.length}\n`;
441
- }
442
- if (complianceIssues.customUtils.length > 0) {
443
- report += ` - Custom utilities: ${complianceIssues.customUtils.length}\n`;
444
- }
445
- if (complianceIssues.supabaseClient.length > 0) {
446
- report += ` - Supabase client issues: ${complianceIssues.supabaseClient.length}\n`;
447
- }
448
- if (complianceIssues.rbacSetup.length > 0) {
449
- report += ` - RBAC setup issues: ${complianceIssues.rbacSetup.length}\n`;
450
- }
451
- if (complianceIssues.rbacPermission && complianceIssues.rbacPermission.length > 0) {
452
- report += ` - RBAC permission usage issues: ${complianceIssues.rbacPermission.length}\n`;
453
- }
454
- if (complianceIssues.providers.length > 0) {
455
- report += ` - Provider issues: ${complianceIssues.providers.length}\n`;
456
- }
457
- if (complianceIssues.coreStyles.length > 0) {
458
- report += ` - Core styles issues: ${complianceIssues.coreStyles.length}\n`;
459
- }
460
- if (complianceIssues.inlineStyles.length > 0) {
461
- report += ` - Inline styles: ${complianceIssues.inlineStyles.length}\n`;
462
- }
463
- }
464
-
465
- // Standards Audit Summary
466
- if (standardsIssuesCount === 0) {
467
- report += `- ✅ Standards Audit: 0 issues\n`;
468
- } else {
469
- report += `- ❌ Standards Audit: ${standardsIssuesCount} issue(s)\n`;
470
- if (standardsIssues.cursorRuleset.length > 0) {
471
- report += ` - Cursor ruleset: ${standardsIssues.cursorRuleset.length}\n`;
472
- }
473
- if (standardsIssues.typescriptConfig.length > 0) {
474
- report += ` - TypeScript config: ${standardsIssues.typescriptConfig.length}\n`;
475
- }
476
- if (standardsIssues.anyTypes.length > 0) {
477
- report += ` - Any types: ${standardsIssues.anyTypes.length}\n`;
478
- }
479
- if (standardsIssues.namingConvention.length > 0) {
480
- report += ` - Naming conventions: ${standardsIssues.namingConvention.length}\n`;
481
- }
482
- if (standardsIssues.rlsPolicy.length > 0) {
483
- report += ` - RLS policies: ${standardsIssues.rlsPolicy.length}\n`;
484
- }
485
- if (standardsIssues.rpcNaming.length > 0) {
486
- report += ` - RPC naming: ${standardsIssues.rpcNaming.length}\n`;
487
- }
488
- if (standardsIssues.testingConfig.length > 0) {
489
- report += ` - Testing config: ${standardsIssues.testingConfig.length}\n`;
490
- }
491
- if (standardsIssues.inputValidation.length > 0) {
492
- report += ` - Input validation: ${standardsIssues.inputValidation.length}\n`;
493
- }
494
- if (standardsIssues.loggingSecurity.length > 0) {
495
- report += ` - Logging security: ${standardsIssues.loggingSecurity.length}\n`;
496
- }
497
- if (standardsIssues.errorMessages.length > 0) {
498
- report += ` - Error messages: ${standardsIssues.errorMessages.length}\n`;
499
- }
500
- }
501
-
502
- // RBAC Audit Summary
503
- if (rbacIssuesCount === 0) {
504
- report += `- ✅ RBAC Audit: 0 issues\n`;
505
- } else {
506
- report += `- ❌ RBAC Audit: ${rbacIssuesCount} issue(s)\n`;
507
- if (rbacIssues.rbacPageGuard.length > 0) {
508
- report += ` - PagePermissionGuard issues: ${rbacIssues.rbacPageGuard.length}\n`;
509
- }
510
- if (rbacIssues.rbacWrapperComponent.length > 0) {
511
- report += ` - Wrapper components: ${rbacIssues.rbacWrapperComponent.length}\n`;
512
- }
513
- if (rbacIssues.rbacWrapperFunction.length > 0) {
514
- report += ` - Wrapper functions: ${rbacIssues.rbacWrapperFunction.length}\n`;
515
- }
516
- if (rbacIssues.rbacResourceNames.length > 0) {
517
- report += ` - RESOURCE_NAMES constants: ${rbacIssues.rbacResourceNames.length}\n`;
518
- }
519
- if (rbacIssues.rbacAccessDenied.length > 0) {
520
- report += ` - AccessDenied component: ${rbacIssues.rbacAccessDenied.length}\n`;
521
- }
522
- if (rbacIssues.rbacDirectRPC.length > 0) {
523
- report += ` - Direct RBAC RPC calls: ${rbacIssues.rbacDirectRPC.length}\n`;
524
- }
525
- if (rbacIssues.rbacDirectTable.length > 0) {
526
- report += ` - Direct RBAC table queries: ${rbacIssues.rbacDirectTable.length}\n`;
527
- }
528
- if (rbacIssues.rbacHardcodedRole.length > 0) {
529
- report += ` - Hardcoded role checks: ${rbacIssues.rbacHardcodedRole.length}\n`;
530
- }
531
- if (rbacIssues.rbacEnforcePermissions.length > 0) {
532
- report += ` - enforcePermissions config: ${rbacIssues.rbacEnforcePermissions.length}\n`;
533
- }
534
- if (rbacIssues.rbacEdgeFunction.length > 0) {
535
- report += ` - Edge Functions RBAC: ${rbacIssues.rbacEdgeFunction.length}\n`;
536
- }
537
- }
538
-
539
- report += `\n- **Total Issues: ${totalIssues}**\n\n`;
540
- }
541
-
542
- report += `---\n\n`;
543
-
544
- // Detailed Issues - Grouped by Audit Type
545
-
546
- // Dependency Audit Issues
547
- if (dependencyIssuesCount > 0) {
548
- report += `## 📦 Dependency Audit Issues\n\n`;
549
-
550
- // Included Dependencies
551
- if (issues.includedDeps.length > 0) {
552
- report += `### ❌ Included Dependencies (MUST REMOVE)\n\n`;
553
- report += `These packages are already included in pace-core and should NOT be installed:\n\n`;
554
- issues.includedDeps.forEach(issue => {
555
- report += `- **${issue.package}**@${issue.installed} (in ${issue.location})\n`;
556
- report += ` - **Problem:** Should NOT be installed (already included in pace-core)\n`;
557
- report += ` - **Fix:** Run \`npm uninstall ${issue.package}\`\n\n`;
558
- });
559
- }
560
-
561
- // Missing Required
562
- if (issues.missingRequired.length > 0) {
563
- report += `### ❌ Missing Required Dependencies\n\n`;
564
- report += `These packages are required for pace-core to function:\n\n`;
565
- issues.missingRequired.forEach(issue => {
566
- report += `- **${issue.package}** (required: ${issue.required})\n`;
567
- report += ` - **Problem:** Required dependency is missing\n`;
568
- report += ` - **Fix:** Run \`npm install ${issue.package}@${issue.required}\`\n\n`;
569
- });
570
- }
571
-
572
- // Version Issues
573
- if (issues.versionIssues.length > 0) {
574
- report += `### ⚠️ Version Issues\n\n`;
575
- issues.versionIssues.forEach(issue => {
576
- report += `- **${issue.package}**\n`;
577
- report += ` - **Installed:** ${issue.installed}\n`;
578
- report += ` - **Required:** ${issue.required}\n`;
579
- report += ` - **Problem:** Version mismatch\n`;
580
- report += ` - **Fix:** Run \`npm install ${issue.package}@${issue.required}\`\n\n`;
581
- });
582
- }
583
-
584
- // Wrong Location
585
- if (issues.wrongLocation.length > 0) {
586
- report += `### ⚠️ Dependencies in Wrong Location\n\n`;
587
- issues.wrongLocation.forEach(issue => {
588
- report += `- **${issue.package}**\n`;
589
- report += ` - **Currently in:** ${issue.current}\n`;
590
- report += ` - **Should be in:** ${issue.shouldBe}\n`;
591
- report += ` - **Problem:** Dependency is in the wrong section of package.json\n`;
592
- report += ` - **Fix:**\n`;
593
- report += ` \`\`\`bash\n`;
594
- report += ` npm uninstall ${issue.package}\n`;
595
- report += ` npm install -D ${issue.package}\n`;
596
- report += ` \`\`\`\n\n`;
597
- });
598
- }
599
-
600
- // Vite Aliases
601
- if (issues.viteAliases.length > 0) {
602
- report += `### ❌ Vite Aliases Bypassing pace-core Exports\n\n`;
603
- report += `These Vite aliases bypass pace-core's exports and should be removed:\n\n`;
604
- issues.viteAliases.forEach(issue => {
605
- report += `- **${issue.alias}** → **${issue.target}**\n`;
606
- report += ` - **File:** ${issue.file}${issue.line ? ` (line ${issue.line})` : ''}\n`;
607
- report += ` - **Problem:** This alias bypasses pace-core's export for \`${issue.alias}\`\n`;
608
- report += ` - **Fix:** Remove \`"${issue.alias}": "${issue.target}"\` from your Vite config and use \`${issue.alias}\` directly from pace-core\n\n`;
609
- });
610
- }
611
- }
612
-
613
- // Component Usage Audit Issues
614
- if (componentIssuesCount > 0) {
615
- report += `## 🧩 Component Usage Audit Issues\n\n`;
616
-
617
- // Form Usage Issues
618
- if (issues.formIssues && issues.formIssues.length > 0) {
619
- report += `### ❌ Form Usage Issues (MUST FIX)\n\n`;
620
- report += `These files use plain \`<form>\` tags or direct \`react-hook-form\` imports instead of pace-core Form component:\n\n`;
621
-
622
- // Group by file for better readability
623
- const issuesByFile = {};
624
- issues.formIssues.forEach(issue => {
625
- if (!issuesByFile[issue.file]) {
626
- issuesByFile[issue.file] = [];
627
- }
628
- issuesByFile[issue.file].push(issue);
629
- });
630
-
631
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
632
- report += `#### ${file}\n\n`;
633
- fileIssues.forEach(issue => {
634
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
635
- if (issue.code) {
636
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
637
- }
638
- });
639
- report += `\n`;
640
- });
641
-
642
- report += `**Fix:** Replace with pace-core Form component. See migration guide below.\n\n`;
643
- }
644
- }
645
-
646
- // Compliance Audit Issues
647
- if (complianceIssuesCount > 0) {
648
- report += `## 🔒 Compliance Audit Issues\n\n`;
649
-
650
- // Native Elements
651
- if (complianceIssues.nativeElements.length > 0) {
652
- report += `### ❌ Native HTML Elements (MUST FIX)\n\n`;
653
- report += `These files use native HTML elements when pace-core components exist:\n\n`;
654
-
655
- const issuesByFile = {};
656
- complianceIssues.nativeElements.forEach(issue => {
657
- if (!issuesByFile[issue.file]) {
658
- issuesByFile[issue.file] = [];
659
- }
660
- issuesByFile[issue.file].push(issue);
661
- });
662
-
663
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
664
- report += `#### ${file}\n\n`;
665
- fileIssues.forEach(issue => {
666
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
667
- if (issue.code) {
668
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
669
- }
670
- report += ` - **Fix:** ${issue.fix}\n\n`;
671
- });
672
- });
673
- }
674
-
675
- // Restricted Imports
676
- if (complianceIssues.restrictedImports.length > 0) {
677
- report += `### ❌ Restricted Library Imports (MUST FIX)\n\n`;
678
- report += `These files import directly from restricted libraries. Use pace-core wrappers instead:\n\n`;
679
-
680
- const issuesByFile = {};
681
- complianceIssues.restrictedImports.forEach(issue => {
682
- if (!issuesByFile[issue.file]) {
683
- issuesByFile[issue.file] = [];
684
- }
685
- issuesByFile[issue.file].push(issue);
686
- });
687
-
688
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
689
- report += `#### ${file}\n\n`;
690
- fileIssues.forEach(issue => {
691
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
692
- if (issue.code) {
693
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
694
- }
695
- report += ` - **Fix:** ${issue.fix}\n\n`;
696
- });
697
- });
698
- }
699
-
700
- // Custom Hooks
701
- if (complianceIssues.customHooks.length > 0) {
702
- report += `### ⚠️ Custom Hooks (Consider Using pace-core)\n\n`;
703
- report += `These files define custom hooks that pace-core provides:\n\n`;
704
-
705
- const issuesByFile = {};
706
- complianceIssues.customHooks.forEach(issue => {
707
- if (!issuesByFile[issue.file]) {
708
- issuesByFile[issue.file] = [];
709
- }
710
- issuesByFile[issue.file].push(issue);
711
- });
712
-
713
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
714
- report += `#### ${file}\n\n`;
715
- fileIssues.forEach(issue => {
716
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
717
- if (issue.code) {
718
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
719
- }
720
- report += ` - **Fix:** ${issue.fix}\n\n`;
721
- });
722
- });
723
- }
724
-
725
- // Custom Utils
726
- if (complianceIssues.customUtils.length > 0) {
727
- report += `### ⚠️ Custom Utilities (Consider Using pace-core)\n\n`;
728
- report += `These files define custom utilities that pace-core provides:\n\n`;
729
-
730
- const issuesByFile = {};
731
- complianceIssues.customUtils.forEach(issue => {
732
- if (!issuesByFile[issue.file]) {
733
- issuesByFile[issue.file] = [];
734
- }
735
- issuesByFile[issue.file].push(issue);
736
- });
737
-
738
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
739
- report += `#### ${file}\n\n`;
740
- fileIssues.forEach(issue => {
741
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
742
- if (issue.code) {
743
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
744
- }
745
- report += ` - **Fix:** ${issue.fix}\n\n`;
746
- });
747
- });
748
- }
749
-
750
- // Supabase Client
751
- if (complianceIssues.supabaseClient.length > 0) {
752
- report += `### ❌ Supabase Client Usage Issues (MUST FIX)\n\n`;
753
- report += `These files have Supabase client usage issues:\n\n`;
754
-
755
- const issuesByFile = {};
756
- complianceIssues.supabaseClient.forEach(issue => {
757
- if (!issuesByFile[issue.file]) {
758
- issuesByFile[issue.file] = [];
759
- }
760
- issuesByFile[issue.file].push(issue);
761
- });
762
-
763
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
764
- report += `#### ${file}\n\n`;
765
- fileIssues.forEach(issue => {
766
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
767
- if (issue.code) {
768
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
769
- }
770
- report += ` - **Fix:** ${issue.fix}\n\n`;
771
- });
772
- });
773
- }
774
-
775
- // RBAC Permission Usage
776
- if (complianceIssues.rbacPermission && complianceIssues.rbacPermission.length > 0) {
777
- report += `### ❌ RBAC Permission Usage Issues (MUST FIX)\n\n`;
778
- report += `These issues can cause false negative permission checks when scope resolution is still in progress.\n\n`;
779
- complianceIssues.rbacPermission.forEach(issue => {
780
- report += `**${issue.file}:${issue.line}** - ${issue.severity.toUpperCase()}\n`;
781
- report += `${issue.message}\n\n`;
782
- if (issue.fix) {
783
- report += `**Fix:** ${issue.fix}\n\n`;
784
- }
785
- if (issue.code) {
786
- report += `\`\`\`typescript\n${issue.code}\n\`\`\`\n\n`;
787
- }
788
- report += `---\n\n`;
789
- });
790
- }
791
-
792
- // RBAC Setup
793
- if (complianceIssues.rbacSetup.length > 0) {
794
- report += `### ❌ RBAC Setup Issues (MUST FIX)\n\n`;
795
- complianceIssues.rbacSetup.forEach(issue => {
796
- report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
797
- report += ` - **Fix:** ${issue.fix}\n\n`;
798
- });
799
- }
800
-
801
- // Providers
802
- if (complianceIssues.providers.length > 0) {
803
- report += `### ❌ Provider Usage Issues (MUST FIX)\n\n`;
804
- complianceIssues.providers.forEach(issue => {
805
- report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
806
- report += ` - **Fix:** ${issue.fix}\n\n`;
807
- });
808
- }
809
-
810
- // Core Styles
811
- if (complianceIssues.coreStyles.length > 0) {
812
- report += `### ❌ Core Styles Import Issues (MUST FIX)\n\n`;
813
- complianceIssues.coreStyles.forEach(issue => {
814
- report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
815
- report += ` - **Fix:** ${issue.fix}\n\n`;
816
- });
817
- }
818
-
819
- // Inline Styles
820
- if (complianceIssues.inlineStyles.length > 0) {
821
- report += `### ❌ Inline Styles (MUST FIX)\n\n`;
822
- report += `These files use inline styles. Use pace-core components or Tailwind classes instead:\n\n`;
823
-
824
- const issuesByFile = {};
825
- complianceIssues.inlineStyles.forEach(issue => {
826
- if (!issuesByFile[issue.file]) {
827
- issuesByFile[issue.file] = [];
828
- }
829
- issuesByFile[issue.file].push(issue);
830
- });
831
-
832
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
833
- report += `#### ${file}\n\n`;
834
- fileIssues.forEach(issue => {
835
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
836
- if (issue.code) {
837
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
838
- }
839
- report += ` - **Fix:** ${issue.fix}\n\n`;
840
- });
841
- });
842
- }
843
- }
844
-
845
- // Standards Audit Issues
846
- if (standardsIssuesCount > 0) {
847
- report += `## 📋 Standards Audit Issues\n\n`;
848
-
849
- // Cursor Ruleset
850
- if (standardsIssues.cursorRuleset.length > 0) {
851
- report += `### ❌ Cursor Ruleset Issues (MUST FIX)\n\n`;
852
- report += `Required rule files are missing from .cursor/rules/ directory:\n\n`;
853
- standardsIssues.cursorRuleset.forEach(issue => {
854
- report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
855
- report += ` - **Fix:** ${issue.fix}\n\n`;
856
- });
857
- }
858
-
859
- // TypeScript Config
860
- if (standardsIssues.typescriptConfig.length > 0) {
861
- report += `### ❌ TypeScript Configuration Issues (MUST FIX)\n\n`;
862
- standardsIssues.typescriptConfig.forEach(issue => {
863
- report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
864
- if (issue.code) {
865
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
866
- }
867
- report += ` - **Fix:** ${issue.fix}\n\n`;
868
- });
869
- }
870
-
871
- // Any Types
872
- if (standardsIssues.anyTypes.length > 0) {
873
- report += `### ⚠️ Any Types Detected\n\n`;
874
- report += `These files use 'any' types. Consider using 'unknown' or proper types:\n\n`;
875
-
876
- const issuesByFile = {};
877
- standardsIssues.anyTypes.forEach(issue => {
878
- if (!issuesByFile[issue.file]) {
879
- issuesByFile[issue.file] = [];
880
- }
881
- issuesByFile[issue.file].push(issue);
882
- });
883
-
884
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
885
- report += `#### ${file}\n\n`;
886
- fileIssues.forEach(issue => {
887
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
888
- if (issue.code) {
889
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
890
- }
891
- report += ` - **Fix:** ${issue.fix}\n\n`;
892
- });
893
- });
894
- }
895
-
896
- // Naming Conventions
897
- if (standardsIssues.namingConvention.length > 0) {
898
- report += `### ⚠️ Naming Convention Issues\n\n`;
899
- report += `These files have naming convention violations:\n\n`;
900
-
901
- const issuesByFile = {};
902
- standardsIssues.namingConvention.forEach(issue => {
903
- if (!issuesByFile[issue.file]) {
904
- issuesByFile[issue.file] = [];
905
- }
906
- issuesByFile[issue.file].push(issue);
907
- });
908
-
909
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
910
- report += `#### ${file}\n\n`;
911
- fileIssues.forEach(issue => {
912
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
913
- if (issue.code) {
914
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
915
- }
916
- report += ` - **Fix:** ${issue.fix}\n\n`;
917
- });
918
- });
919
- }
920
-
921
- // RLS Policies
922
- if (standardsIssues.rlsPolicy.length > 0) {
923
- report += `### ❌ RLS Policy Issues (MUST FIX)\n\n`;
924
- report += `These SQL migrations have RLS policy violations:\n\n`;
925
-
926
- const issuesByFile = {};
927
- standardsIssues.rlsPolicy.forEach(issue => {
928
- if (!issuesByFile[issue.file]) {
929
- issuesByFile[issue.file] = [];
930
- }
931
- issuesByFile[issue.file].push(issue);
932
- });
933
-
934
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
935
- report += `#### ${file}\n\n`;
936
- fileIssues.forEach(issue => {
937
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
938
- if (issue.code) {
939
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
940
- }
941
- report += ` - **Fix:** ${issue.fix}\n\n`;
942
- });
943
- });
944
- }
945
-
946
- // RPC Naming
947
- if (standardsIssues.rpcNaming.length > 0) {
948
- report += `### ❌ RPC Naming Issues (MUST FIX)\n\n`;
949
- report += `These SQL migrations have RPC function naming violations:\n\n`;
950
-
951
- const issuesByFile = {};
952
- standardsIssues.rpcNaming.forEach(issue => {
953
- if (!issuesByFile[issue.file]) {
954
- issuesByFile[issue.file] = [];
955
- }
956
- issuesByFile[issue.file].push(issue);
957
- });
958
-
959
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
960
- report += `#### ${file}\n\n`;
961
- fileIssues.forEach(issue => {
962
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
963
- if (issue.code) {
964
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
965
- }
966
- report += ` - **Fix:** ${issue.fix}\n\n`;
967
- });
968
- });
969
- }
970
-
971
- // Testing Config
972
- if (standardsIssues.testingConfig.length > 0) {
973
- report += `### ❌ Testing Configuration Issues (MUST FIX)\n\n`;
974
- standardsIssues.testingConfig.forEach(issue => {
975
- report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
976
- if (issue.code) {
977
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
978
- }
979
- report += ` - **Fix:** ${issue.fix}\n\n`;
980
- });
981
- }
982
-
983
- // Input Validation (heuristic)
984
- if (standardsIssues.inputValidation.length > 0) {
985
- report += `### ⚠️ Input Validation Issues (Heuristic)\n\n`;
986
- report += `These files may be missing Zod validation. This is a heuristic check and may have false positives:\n\n`;
987
-
988
- const issuesByFile = {};
989
- standardsIssues.inputValidation.forEach(issue => {
990
- if (!issuesByFile[issue.file]) {
991
- issuesByFile[issue.file] = [];
992
- }
993
- issuesByFile[issue.file].push(issue);
994
- });
995
-
996
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
997
- report += `#### ${file}\n\n`;
998
- fileIssues.forEach(issue => {
999
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1000
- if (issue.code) {
1001
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1002
- }
1003
- report += ` - **Fix:** ${issue.fix}\n\n`;
1004
- });
1005
- });
1006
- }
1007
-
1008
- // Logging Security (heuristic)
1009
- if (standardsIssues.loggingSecurity.length > 0) {
1010
- report += `### ⚠️ Logging Security Issues (Heuristic)\n\n`;
1011
- report += `These files may be logging sensitive data. This is a heuristic check and may have false positives:\n\n`;
1012
-
1013
- const issuesByFile = {};
1014
- standardsIssues.loggingSecurity.forEach(issue => {
1015
- if (!issuesByFile[issue.file]) {
1016
- issuesByFile[issue.file] = [];
1017
- }
1018
- issuesByFile[issue.file].push(issue);
1019
- });
1020
-
1021
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1022
- report += `#### ${file}\n\n`;
1023
- fileIssues.forEach(issue => {
1024
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1025
- if (issue.code) {
1026
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1027
- }
1028
- report += ` - **Fix:** ${issue.fix}\n\n`;
1029
- });
1030
- });
1031
- }
1032
-
1033
- // Error Messages (heuristic)
1034
- if (standardsIssues.errorMessages.length > 0) {
1035
- report += `### ⚠️ Error Message Safety Issues (Heuristic)\n\n`;
1036
- report += `These files may expose internal details in error messages. This is a heuristic check and may have false positives:\n\n`;
1037
-
1038
- const issuesByFile = {};
1039
- standardsIssues.errorMessages.forEach(issue => {
1040
- if (!issuesByFile[issue.file]) {
1041
- issuesByFile[issue.file] = [];
1042
- }
1043
- issuesByFile[issue.file].push(issue);
1044
- });
1045
-
1046
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1047
- report += `#### ${file}\n\n`;
1048
- fileIssues.forEach(issue => {
1049
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1050
- if (issue.code) {
1051
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1052
- }
1053
- report += ` - **Fix:** ${issue.fix}\n\n`;
1054
- });
1055
- });
1056
- }
1057
- }
1058
-
1059
- // RBAC Audit Issues
1060
- if (rbacIssuesCount > 0) {
1061
- report += `## 🔐 RBAC Audit Issues\n\n`;
1062
- report += `These issues relate to RBAC (Role-Based Access Control) compliance. See [09-rbac-compliance.mdc](../../packages/core/cursor-rules/09-rbac-compliance.mdc) for complete documentation.\n\n`;
1063
-
1064
- // PagePermissionGuard Issues
1065
- if (rbacIssues.rbacPageGuard.length > 0) {
1066
- report += `### ❌ PagePermissionGuard Issues (MUST FIX)\n\n`;
1067
- report += `These pages are missing PagePermissionGuard wrapper or using it incorrectly:\n\n`;
1068
-
1069
- const issuesByFile = {};
1070
- rbacIssues.rbacPageGuard.forEach(issue => {
1071
- if (!issuesByFile[issue.file]) {
1072
- issuesByFile[issue.file] = [];
1073
- }
1074
- issuesByFile[issue.file].push(issue);
1075
- });
1076
-
1077
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1078
- report += `#### ${file}\n\n`;
1079
- fileIssues.forEach(issue => {
1080
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1081
- if (issue.code) {
1082
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1083
- }
1084
- report += ` - **Fix:** ${issue.fix}\n\n`;
1085
- });
1086
- });
1087
- }
1088
-
1089
- // Wrapper Components
1090
- if (rbacIssues.rbacWrapperComponent.length > 0) {
1091
- report += `### ❌ Wrapper Components Around PagePermissionGuard (MUST FIX)\n\n`;
1092
- report += `These components wrap PagePermissionGuard, which is forbidden:\n\n`;
1093
-
1094
- const issuesByFile = {};
1095
- rbacIssues.rbacWrapperComponent.forEach(issue => {
1096
- if (!issuesByFile[issue.file]) {
1097
- issuesByFile[issue.file] = [];
1098
- }
1099
- issuesByFile[issue.file].push(issue);
1100
- });
1101
-
1102
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1103
- report += `#### ${file}\n\n`;
1104
- fileIssues.forEach(issue => {
1105
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1106
- if (issue.code) {
1107
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1108
- }
1109
- report += ` - **Fix:** ${issue.fix}\n\n`;
1110
- });
1111
- });
1112
- }
1113
-
1114
- // Wrapper Functions
1115
- if (rbacIssues.rbacWrapperFunction.length > 0) {
1116
- report += `### ❌ Wrapper Functions Around Permission Hooks (MUST FIX)\n\n`;
1117
- report += `These functions wrap permission hooks, which is forbidden:\n\n`;
1118
-
1119
- const issuesByFile = {};
1120
- rbacIssues.rbacWrapperFunction.forEach(issue => {
1121
- if (!issuesByFile[issue.file]) {
1122
- issuesByFile[issue.file] = [];
1123
- }
1124
- issuesByFile[issue.file].push(issue);
1125
- });
1126
-
1127
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1128
- report += `#### ${file}\n\n`;
1129
- fileIssues.forEach(issue => {
1130
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1131
- if (issue.code) {
1132
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1133
- }
1134
- report += ` - **Fix:** ${issue.fix}\n\n`;
1135
- });
1136
- });
1137
- }
1138
-
1139
- // RESOURCE_NAMES Constants
1140
- if (rbacIssues.rbacResourceNames.length > 0) {
1141
- report += `### ❌ RESOURCE_NAMES Constants Usage (MUST FIX)\n\n`;
1142
- report += `These files use string literals in useResourcePermissions instead of RESOURCE_NAMES constants:\n\n`;
1143
-
1144
- const issuesByFile = {};
1145
- rbacIssues.rbacResourceNames.forEach(issue => {
1146
- if (!issuesByFile[issue.file]) {
1147
- issuesByFile[issue.file] = [];
1148
- }
1149
- issuesByFile[issue.file].push(issue);
1150
- });
1151
-
1152
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1153
- report += `#### ${file}\n\n`;
1154
- fileIssues.forEach(issue => {
1155
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1156
- if (issue.code) {
1157
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1158
- }
1159
- report += ` - **Fix:** ${issue.fix}\n\n`;
1160
- });
1161
- });
1162
- }
1163
-
1164
- // AccessDenied Component
1165
- if (rbacIssues.rbacAccessDenied.length > 0) {
1166
- report += `### ⚠️ AccessDenied Component Usage\n\n`;
1167
- report += `These files use custom access denied components instead of pace-core AccessDenied:\n\n`;
1168
-
1169
- const issuesByFile = {};
1170
- rbacIssues.rbacAccessDenied.forEach(issue => {
1171
- if (!issuesByFile[issue.file]) {
1172
- issuesByFile[issue.file] = [];
1173
- }
1174
- issuesByFile[issue.file].push(issue);
1175
- });
1176
-
1177
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1178
- report += `#### ${file}\n\n`;
1179
- fileIssues.forEach(issue => {
1180
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1181
- if (issue.code) {
1182
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1183
- }
1184
- report += ` - **Fix:** ${issue.fix}\n\n`;
1185
- });
1186
- });
1187
- }
1188
-
1189
- // Direct RBAC RPC Calls
1190
- if (rbacIssues.rbacDirectRPC.length > 0) {
1191
- report += `### ❌ Direct RBAC RPC Calls (SECURITY CRITICAL)\n\n`;
1192
- report += `These files call RBAC RPC functions directly, bypassing security validation:\n\n`;
1193
-
1194
- const issuesByFile = {};
1195
- rbacIssues.rbacDirectRPC.forEach(issue => {
1196
- if (!issuesByFile[issue.file]) {
1197
- issuesByFile[issue.file] = [];
1198
- }
1199
- issuesByFile[issue.file].push(issue);
1200
- });
1201
-
1202
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1203
- report += `#### ${file}\n\n`;
1204
- fileIssues.forEach(issue => {
1205
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1206
- if (issue.code) {
1207
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1208
- }
1209
- report += ` - **Fix:** ${issue.fix}\n\n`;
1210
- });
1211
- });
1212
- }
1213
-
1214
- // Direct RBAC Table Queries
1215
- if (rbacIssues.rbacDirectTable.length > 0) {
1216
- report += `### ❌ Direct RBAC Table Queries (SECURITY CRITICAL)\n\n`;
1217
- report += `These files query RBAC tables directly without useSecureSupabase:\n\n`;
1218
-
1219
- const issuesByFile = {};
1220
- rbacIssues.rbacDirectTable.forEach(issue => {
1221
- if (!issuesByFile[issue.file]) {
1222
- issuesByFile[issue.file] = [];
1223
- }
1224
- issuesByFile[issue.file].push(issue);
1225
- });
1226
-
1227
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1228
- report += `#### ${file}\n\n`;
1229
- fileIssues.forEach(issue => {
1230
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1231
- if (issue.code) {
1232
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1233
- }
1234
- report += ` - **Fix:** ${issue.fix}\n\n`;
1235
- });
1236
- });
1237
- }
1238
-
1239
- // Hardcoded Role Checks
1240
- if (rbacIssues.rbacHardcodedRole.length > 0) {
1241
- report += `### ❌ Hardcoded Role Checks (SECURITY CRITICAL)\n\n`;
1242
- report += `These files use hardcoded role comparisons instead of pace-core APIs:\n\n`;
1243
-
1244
- const issuesByFile = {};
1245
- rbacIssues.rbacHardcodedRole.forEach(issue => {
1246
- if (!issuesByFile[issue.file]) {
1247
- issuesByFile[issue.file] = [];
1248
- }
1249
- issuesByFile[issue.file].push(issue);
1250
- });
1251
-
1252
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1253
- report += `#### ${file}\n\n`;
1254
- fileIssues.forEach(issue => {
1255
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1256
- if (issue.code) {
1257
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1258
- }
1259
- report += ` - **Fix:** ${issue.fix}\n\n`;
1260
- });
1261
- });
1262
- }
1263
-
1264
- // enforcePermissions Configuration
1265
- if (rbacIssues.rbacEnforcePermissions.length > 0) {
1266
- report += `### ❌ enforcePermissions Configuration Issues\n\n`;
1267
- report += `These files have incorrect enforcePermissions configuration:\n\n`;
1268
-
1269
- rbacIssues.rbacEnforcePermissions.forEach(issue => {
1270
- report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
1271
- if (issue.code) {
1272
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1273
- }
1274
- report += ` - **Fix:** ${issue.fix}\n\n`;
1275
- });
1276
- }
1277
-
1278
- // Edge Functions RBAC
1279
- if (rbacIssues.rbacEdgeFunction.length > 0) {
1280
- report += `### ❌ Edge Functions RBAC Issues (SECURITY CRITICAL)\n\n`;
1281
- report += `These Edge Functions have RBAC compliance issues:\n\n`;
1282
-
1283
- rbacIssues.rbacEdgeFunction.forEach(issue => {
1284
- report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
1285
- if (issue.code) {
1286
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1287
- }
1288
- report += ` - **Fix:** ${issue.fix}\n\n`;
1289
- });
1290
- }
1291
- }
1292
-
1293
- // RBAC Audit Issues
1294
- if (rbacIssuesCount > 0) {
1295
- report += `## 🔐 RBAC Audit Issues\n\n`;
1296
- report += `These issues relate to RBAC (Role-Based Access Control) compliance. See [09-rbac-compliance.mdc](../../packages/core/cursor-rules/09-rbac-compliance.mdc) for complete documentation.\n\n`;
1297
-
1298
- // PagePermissionGuard Issues
1299
- if (rbacIssues.rbacPageGuard.length > 0) {
1300
- report += `### ❌ PagePermissionGuard Issues (MUST FIX)\n\n`;
1301
- report += `These pages are missing PagePermissionGuard wrapper or using it incorrectly:\n\n`;
1302
-
1303
- const issuesByFile = {};
1304
- rbacIssues.rbacPageGuard.forEach(issue => {
1305
- if (!issuesByFile[issue.file]) {
1306
- issuesByFile[issue.file] = [];
1307
- }
1308
- issuesByFile[issue.file].push(issue);
1309
- });
1310
-
1311
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1312
- report += `#### ${file}\n\n`;
1313
- fileIssues.forEach(issue => {
1314
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1315
- if (issue.code) {
1316
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1317
- }
1318
- report += ` - **Fix:** ${issue.fix}\n\n`;
1319
- });
1320
- });
1321
- }
1322
-
1323
- // Wrapper Components
1324
- if (rbacIssues.rbacWrapperComponent.length > 0) {
1325
- report += `### ❌ Wrapper Components Around PagePermissionGuard (MUST FIX)\n\n`;
1326
- report += `These components wrap PagePermissionGuard, which is forbidden:\n\n`;
1327
-
1328
- const issuesByFile = {};
1329
- rbacIssues.rbacWrapperComponent.forEach(issue => {
1330
- if (!issuesByFile[issue.file]) {
1331
- issuesByFile[issue.file] = [];
1332
- }
1333
- issuesByFile[issue.file].push(issue);
1334
- });
1335
-
1336
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1337
- report += `#### ${file}\n\n`;
1338
- fileIssues.forEach(issue => {
1339
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1340
- if (issue.code) {
1341
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1342
- }
1343
- report += ` - **Fix:** ${issue.fix}\n\n`;
1344
- });
1345
- });
1346
- }
1347
-
1348
- // Wrapper Functions
1349
- if (rbacIssues.rbacWrapperFunction.length > 0) {
1350
- report += `### ❌ Wrapper Functions Around Permission Hooks (MUST FIX)\n\n`;
1351
- report += `These functions wrap permission hooks, which is forbidden:\n\n`;
1352
-
1353
- const issuesByFile = {};
1354
- rbacIssues.rbacWrapperFunction.forEach(issue => {
1355
- if (!issuesByFile[issue.file]) {
1356
- issuesByFile[issue.file] = [];
1357
- }
1358
- issuesByFile[issue.file].push(issue);
1359
- });
1360
-
1361
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1362
- report += `#### ${file}\n\n`;
1363
- fileIssues.forEach(issue => {
1364
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1365
- if (issue.code) {
1366
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1367
- }
1368
- report += ` - **Fix:** ${issue.fix}\n\n`;
1369
- });
1370
- });
1371
- }
1372
-
1373
- // RESOURCE_NAMES Constants
1374
- if (rbacIssues.rbacResourceNames.length > 0) {
1375
- report += `### ❌ RESOURCE_NAMES Constants Usage (MUST FIX)\n\n`;
1376
- report += `These files use string literals in useResourcePermissions instead of RESOURCE_NAMES constants:\n\n`;
1377
-
1378
- const issuesByFile = {};
1379
- rbacIssues.rbacResourceNames.forEach(issue => {
1380
- if (!issuesByFile[issue.file]) {
1381
- issuesByFile[issue.file] = [];
1382
- }
1383
- issuesByFile[issue.file].push(issue);
1384
- });
1385
-
1386
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1387
- report += `#### ${file}\n\n`;
1388
- fileIssues.forEach(issue => {
1389
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1390
- if (issue.code) {
1391
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1392
- }
1393
- report += ` - **Fix:** ${issue.fix}\n\n`;
1394
- });
1395
- });
1396
- }
1397
-
1398
- // AccessDenied Component
1399
- if (rbacIssues.rbacAccessDenied.length > 0) {
1400
- report += `### ⚠️ AccessDenied Component Usage\n\n`;
1401
- report += `These files use custom access denied components instead of pace-core AccessDenied:\n\n`;
1402
-
1403
- const issuesByFile = {};
1404
- rbacIssues.rbacAccessDenied.forEach(issue => {
1405
- if (!issuesByFile[issue.file]) {
1406
- issuesByFile[issue.file] = [];
1407
- }
1408
- issuesByFile[issue.file].push(issue);
1409
- });
1410
-
1411
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1412
- report += `#### ${file}\n\n`;
1413
- fileIssues.forEach(issue => {
1414
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1415
- if (issue.code) {
1416
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1417
- }
1418
- report += ` - **Fix:** ${issue.fix}\n\n`;
1419
- });
1420
- });
1421
- }
1422
-
1423
- // Direct RBAC RPC Calls
1424
- if (rbacIssues.rbacDirectRPC.length > 0) {
1425
- report += `### ❌ Direct RBAC RPC Calls (SECURITY CRITICAL)\n\n`;
1426
- report += `These files call RBAC RPC functions directly, bypassing security validation:\n\n`;
1427
-
1428
- const issuesByFile = {};
1429
- rbacIssues.rbacDirectRPC.forEach(issue => {
1430
- if (!issuesByFile[issue.file]) {
1431
- issuesByFile[issue.file] = [];
1432
- }
1433
- issuesByFile[issue.file].push(issue);
1434
- });
1435
-
1436
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1437
- report += `#### ${file}\n\n`;
1438
- fileIssues.forEach(issue => {
1439
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1440
- if (issue.code) {
1441
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1442
- }
1443
- report += ` - **Fix:** ${issue.fix}\n\n`;
1444
- });
1445
- });
1446
- }
1447
-
1448
- // Direct RBAC Table Queries
1449
- if (rbacIssues.rbacDirectTable.length > 0) {
1450
- report += `### ❌ Direct RBAC Table Queries (SECURITY CRITICAL)\n\n`;
1451
- report += `These files query RBAC tables directly without useSecureSupabase:\n\n`;
1452
-
1453
- const issuesByFile = {};
1454
- rbacIssues.rbacDirectTable.forEach(issue => {
1455
- if (!issuesByFile[issue.file]) {
1456
- issuesByFile[issue.file] = [];
1457
- }
1458
- issuesByFile[issue.file].push(issue);
1459
- });
1460
-
1461
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1462
- report += `#### ${file}\n\n`;
1463
- fileIssues.forEach(issue => {
1464
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1465
- if (issue.code) {
1466
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1467
- }
1468
- report += ` - **Fix:** ${issue.fix}\n\n`;
1469
- });
1470
- });
1471
- }
1472
-
1473
- // Hardcoded Role Checks
1474
- if (rbacIssues.rbacHardcodedRole.length > 0) {
1475
- report += `### ❌ Hardcoded Role Checks (SECURITY CRITICAL)\n\n`;
1476
- report += `These files use hardcoded role comparisons instead of pace-core APIs:\n\n`;
1477
-
1478
- const issuesByFile = {};
1479
- rbacIssues.rbacHardcodedRole.forEach(issue => {
1480
- if (!issuesByFile[issue.file]) {
1481
- issuesByFile[issue.file] = [];
1482
- }
1483
- issuesByFile[issue.file].push(issue);
1484
- });
1485
-
1486
- Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
1487
- report += `#### ${file}\n\n`;
1488
- fileIssues.forEach(issue => {
1489
- report += `- **Line ${issue.line}**: ${issue.message}\n`;
1490
- if (issue.code) {
1491
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1492
- }
1493
- report += ` - **Fix:** ${issue.fix}\n\n`;
1494
- });
1495
- });
1496
- }
1497
-
1498
- // enforcePermissions Configuration
1499
- if (rbacIssues.rbacEnforcePermissions.length > 0) {
1500
- report += `### ❌ enforcePermissions Configuration Issues\n\n`;
1501
- report += `These files have incorrect enforcePermissions configuration:\n\n`;
1502
-
1503
- rbacIssues.rbacEnforcePermissions.forEach(issue => {
1504
- report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
1505
- if (issue.code) {
1506
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1507
- }
1508
- report += ` - **Fix:** ${issue.fix}\n\n`;
1509
- });
1510
- }
1511
-
1512
- // Edge Functions RBAC
1513
- if (rbacIssues.rbacEdgeFunction.length > 0) {
1514
- report += `### ❌ Edge Functions RBAC Issues (SECURITY CRITICAL)\n\n`;
1515
- report += `These Edge Functions have RBAC compliance issues:\n\n`;
1516
-
1517
- rbacIssues.rbacEdgeFunction.forEach(issue => {
1518
- report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
1519
- if (issue.code) {
1520
- report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
1521
- }
1522
- report += ` - **Fix:** ${issue.fix}\n\n`;
1523
- });
1524
- }
1525
- }
1526
-
1527
- // Missing Optional (informational only)
1528
- if (issues.missingOptional.length > 0) {
1529
- report += `---\n\n`;
1530
- report += `## ℹ️ Missing Optional Dependencies\n\n`;
1531
- report += `These packages are optional (only install if you use these features):\n\n`;
1532
- issues.missingOptional.forEach(issue => {
1533
- report += `- **${issue.package}** (required: ${issue.required})\n`;
1534
- });
1535
- report += `\n`;
1536
- }
1537
-
1538
- // Fix Commands Section
1539
- if (totalIssues > 0) {
1540
- report += `---\n\n`;
1541
- report += `## 🔧 Quick Fix Commands\n\n`;
1542
- report += `Run these commands to fix the issues found:\n\n`;
1543
-
1544
- if (issues.includedDeps.length > 0) {
1545
- const depsToRemove = issues.includedDeps.map(i => i.package).join(' ');
1546
- report += `### Remove Included Dependencies\n\n`;
1547
- report += `\`\`\`bash\n`;
1548
- report += `npm uninstall ${depsToRemove}\n`;
1549
- report += `\`\`\`\n\n`;
1550
- }
1551
-
1552
- if (issues.missingRequired.length > 0) {
1553
- const depsToInstall = issues.missingRequired
1554
- .map(i => `${i.package}@${i.required}`)
1555
- .join(' ');
1556
- report += `### Install Missing Required Dependencies\n\n`;
1557
- report += `\`\`\`bash\n`;
1558
- report += `npm install ${depsToInstall}\n`;
1559
- report += `\`\`\`\n\n`;
1560
- }
1561
-
1562
- if (issues.versionIssues.length > 0) {
1563
- const depsToFix = issues.versionIssues
1564
- .map(i => `${i.package}@${i.required}`)
1565
- .join(' ');
1566
- report += `### Fix Version Ranges\n\n`;
1567
- report += `\`\`\`bash\n`;
1568
- report += `npm install ${depsToFix}\n`;
1569
- report += `\`\`\`\n\n`;
1570
- }
1571
-
1572
- if (issues.wrongLocation.length > 0) {
1573
- issues.wrongLocation.forEach(issue => {
1574
- report += `### Move ${issue.package} to devDependencies\n\n`;
1575
- report += `\`\`\`bash\n`;
1576
- report += `npm uninstall ${issue.package}\n`;
1577
- report += `npm install -D ${issue.package}\n`;
1578
- report += `\`\`\`\n\n`;
1579
- });
1580
- }
1581
-
1582
- if (issues.viteAliases.length > 0) {
1583
- report += `### Fix Vite Aliases\n\n`;
1584
- report += `Remove the following aliases from your \`vite.config.ts\` (or \`vite.config.js\`):\n\n`;
1585
- issues.viteAliases.forEach(issue => {
1586
- report += `- Remove: \`"${issue.alias}": "${issue.target}"\`\n`;
1587
- });
1588
- report += `\n`;
1589
- report += `These aliases bypass pace-core's exports. Use the pace-core exports directly instead.\n\n`;
1590
- }
1591
-
1592
- if (issues.formIssues && issues.formIssues.length > 0) {
1593
- report += `### Fix Form Usage\n\n`;
1594
- report += `Replace plain \`<form>\` tags and direct \`react-hook-form\` usage with pace-core Form component:\n\n`;
1595
- report += `**Before (❌ Wrong):**\n`;
1596
- report += `\`\`\`tsx\n`;
1597
- report += `import { useForm } from 'react-hook-form';\n`;
1598
- report += `\n`;
1599
- report += `function MyForm() {\n`;
1600
- report += ` const { register, handleSubmit } = useForm();\n`;
1601
- report += ` return (\n`;
1602
- report += ` <form onSubmit={handleSubmit(onSubmit)}>\n`;
1603
- report += ` <input {...register('name')} />\n`;
1604
- report += ` <button type="submit">Submit</button>\n`;
1605
- report += ` </form>\n`;
1606
- report += ` );\n`;
1607
- report += `}\n`;
1608
- report += `\`\`\`\n\n`;
1609
- report += `**After (✅ Correct):**\n`;
1610
- report += `\`\`\`tsx\n`;
1611
- report += `import { Form, FormField, Button } from '@jmruthers/pace-core';\n`;
1612
- report += `import { z } from 'zod';\n`;
1613
- report += `\n`;
1614
- report += `const formSchema = z.object({\n`;
1615
- report += ` name: z.string().min(1, 'Name is required'),\n`;
1616
- report += `});\n`;
1617
- report += `\n`;
1618
- report += `function MyForm() {\n`;
1619
- report += ` return (\n`;
1620
- report += ` <Form\n`;
1621
- report += ` schema={formSchema}\n`;
1622
- report += ` defaultValues={{ name: '' }}\n`;
1623
- report += ` onSubmit={(data) => console.log(data)}\n`;
1624
- report += ` >\n`;
1625
- report += ` <FormField name="name" label="Name" />\n`;
1626
- report += ` <Button type="submit">Submit</Button>\n`;
1627
- report += ` </Form>\n`;
1628
- report += ` );\n`;
1629
- report += `}\n`;
1630
- report += `\`\`\`\n\n`;
1631
- report += `**Key Benefits:**\n`;
1632
- report += `- Built-in Zod validation with type safety\n`;
1633
- report += `- Automatic form persistence (session draft)\n`;
1634
- report += `- Consistent styling with pace-core design system\n`;
1635
- report += `- Accessible form structure with error handling\n`;
1636
- report += `- Sensitive field filtering (passwords, etc.)\n\n`;
1637
- report += `**Documentation:** See [pace-core Forms Guide](https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/implementation-guides/forms.md) for complete examples.\n\n`;
1638
- }
1639
- }
1640
-
1641
- report += `---\n\n`;
1642
- report += `*Generated by pace-core audit script*\n`;
1643
-
1644
- return report;
1645
- }
1646
-
1647
- // Main function
1648
- function main() {
1649
- const args = process.argv.slice(2);
1650
- const consumingAppPath = args.find(arg => !arg.startsWith('--')) || process.cwd();
1651
- const outputArg = args.find(arg => arg.startsWith('--output'));
1652
- const outputPath = outputArg ? outputArg.split('=')[1] || 'audit-report.md' : null;
1653
-
1654
- console.log(`${colors.bold}${colors.cyan}pace-core Comprehensive Audit${colors.reset}\n`);
1655
- console.log(`${colors.cyan}${'='.repeat(50)}${colors.reset}\n`);
1656
-
1657
- // Run dependency audit (with Vite alias checks and component audit)
1658
- console.log(`${colors.blue}Running dependency audit...${colors.reset}`);
1659
- const auditResults = runDependencyAuditWithExtras(consumingAppPath, true);
1660
-
1661
- if (auditResults.error) {
1662
- console.error(`${colors.red}Error: ${auditResults.error}${colors.reset}`);
1663
- process.exit(1);
1664
- }
1665
-
1666
- // Display results
1667
- const { projectName, paceCoreVersion, issues } = auditResults;
1668
-
1669
- // Calculate issue counts
1670
- const dependencyIssuesCount = issues.includedDeps.length +
1671
- issues.missingRequired.length +
1672
- issues.versionIssues.length +
1673
- issues.wrongLocation.length +
1674
- issues.viteAliases.length;
1675
-
1676
- const componentIssuesCount = issues.formIssues?.length || 0;
1677
-
1678
- const complianceIssues = issues.complianceIssues || {
1679
- nativeElements: [],
1680
- restrictedImports: [],
1681
- customHooks: [],
1682
- customUtils: [],
1683
- supabaseClient: [],
1684
- rbacSetup: [],
1685
- providers: [],
1686
- coreStyles: [],
1687
- inlineStyles: [],
1688
- };
1689
-
1690
- const complianceIssuesCount = Object.values(complianceIssues).reduce((sum, arr) => sum + arr.length, 0);
1691
-
1692
- const standardsIssues = issues.standardsIssues || {
1693
- cursorRuleset: [],
1694
- typescriptConfig: [],
1695
- anyTypes: [],
1696
- namingConvention: [],
1697
- rlsPolicy: [],
1698
- rpcNaming: [],
1699
- testingConfig: [],
1700
- inputValidation: [],
1701
- loggingSecurity: [],
1702
- errorMessages: [],
1703
- };
1704
-
1705
- const standardsIssuesCount = Object.values(standardsIssues).reduce((sum, arr) => sum + arr.length, 0);
1706
-
1707
- const rbacIssues = issues.rbacIssues || {
1708
- rbacPageGuard: [],
1709
- rbacWrapperComponent: [],
1710
- rbacWrapperFunction: [],
1711
- rbacResourceNames: [],
1712
- rbacAccessDenied: [],
1713
- rbacDirectRPC: [],
1714
- rbacDirectTable: [],
1715
- rbacHardcodedRole: [],
1716
- rbacEnforcePermissions: [],
1717
- rbacEdgeFunction: [],
1718
- };
1719
-
1720
- const rbacIssuesCount = Object.values(rbacIssues).reduce((sum, arr) => sum + arr.length, 0);
1721
-
1722
- const totalIssues = dependencyIssuesCount + componentIssuesCount + complianceIssuesCount + standardsIssuesCount + rbacIssuesCount;
1723
-
1724
- // Display project info
1725
- console.log(`Project: ${colors.bold}${projectName}${colors.reset}`);
1726
- console.log(`pace-core: ${colors.bold}${paceCoreVersion}${colors.reset}\n`);
1727
-
1728
- // Display audit results summary
1729
- console.log(`${colors.bold}Audit Results:${colors.reset}\n`);
1730
-
1731
- // Dependency Audit
1732
- if (dependencyIssuesCount === 0) {
1733
- console.log(` ${colors.green}✅ Dependency Audit: 0 issues${colors.reset}`);
1734
- } else {
1735
- console.log(` ${colors.red}❌ Dependency Audit: ${dependencyIssuesCount} issue(s)${colors.reset}`);
1736
- if (issues.includedDeps.length > 0) {
1737
- console.log(` - Included Dependencies: ${issues.includedDeps.length}`);
1738
- }
1739
- if (issues.missingRequired.length > 0) {
1740
- console.log(` - Missing Required: ${issues.missingRequired.length}`);
1741
- }
1742
- if (issues.versionIssues.length > 0) {
1743
- console.log(` - Version Issues: ${issues.versionIssues.length}`);
1744
- }
1745
- if (issues.wrongLocation.length > 0) {
1746
- console.log(` - Wrong Location: ${issues.wrongLocation.length}`);
1747
- }
1748
- if (issues.viteAliases.length > 0) {
1749
- console.log(` - Vite Aliases: ${issues.viteAliases.length}`);
1750
- }
1751
- }
1752
-
1753
- // Component Usage Audit
1754
- if (componentIssuesCount === 0) {
1755
- console.log(` ${colors.green}✅ Component Usage Audit: 0 issues${colors.reset}`);
1756
- } else {
1757
- const plainFormTagsCount = issues.formIssues.filter(i => i.type === 'plainFormTag').length;
1758
- const reactHookFormImportsCount = issues.formIssues.filter(i => i.type === 'reactHookFormImport').length;
1759
- console.log(` ${colors.red}❌ Component Usage Audit: ${componentIssuesCount} issue(s)${colors.reset}`);
1760
- if (plainFormTagsCount > 0) {
1761
- console.log(` - Plain <form> tags: ${plainFormTagsCount}`);
1762
- }
1763
- if (reactHookFormImportsCount > 0) {
1764
- console.log(` - Direct react-hook-form imports: ${reactHookFormImportsCount}`);
1765
- }
1766
- }
1767
-
1768
- // Compliance Audit
1769
- if (complianceIssuesCount === 0) {
1770
- console.log(` ${colors.green}✅ Compliance Audit: 0 issues${colors.reset}`);
1771
- } else {
1772
- console.log(` ${colors.red}❌ Compliance Audit: ${complianceIssuesCount} issue(s)${colors.reset}`);
1773
- if (complianceIssues.nativeElements.length > 0) {
1774
- console.log(` - Native HTML elements: ${complianceIssues.nativeElements.length}`);
1775
- }
1776
- if (complianceIssues.restrictedImports.length > 0) {
1777
- console.log(` - Restricted imports: ${complianceIssues.restrictedImports.length}`);
1778
- }
1779
- if (complianceIssues.customHooks.length > 0) {
1780
- console.log(` - Custom hooks: ${complianceIssues.customHooks.length}`);
1781
- }
1782
- if (complianceIssues.customUtils.length > 0) {
1783
- console.log(` - Custom utilities: ${complianceIssues.customUtils.length}`);
1784
- }
1785
- if (complianceIssues.supabaseClient.length > 0) {
1786
- console.log(` - Supabase client issues: ${complianceIssues.supabaseClient.length}`);
1787
- }
1788
- if (complianceIssues.rbacSetup.length > 0) {
1789
- console.log(` - RBAC setup issues: ${complianceIssues.rbacSetup.length}`);
1790
- }
1791
- if (complianceIssues.rbacPermission && complianceIssues.rbacPermission.length > 0) {
1792
- console.log(` - RBAC permission usage issues: ${complianceIssues.rbacPermission.length}`);
1793
- }
1794
- if (complianceIssues.providers.length > 0) {
1795
- console.log(` - Provider issues: ${complianceIssues.providers.length}`);
1796
- }
1797
- if (complianceIssues.coreStyles.length > 0) {
1798
- console.log(` - Core styles issues: ${complianceIssues.coreStyles.length}`);
1799
- }
1800
- if (complianceIssues.inlineStyles.length > 0) {
1801
- console.log(` - Inline styles: ${complianceIssues.inlineStyles.length}`);
1802
- }
1803
- }
1804
-
1805
- // Standards Audit
1806
- if (standardsIssuesCount === 0) {
1807
- console.log(` ${colors.green}✅ Standards Audit: 0 issues${colors.reset}`);
1808
- } else {
1809
- console.log(` ${colors.red}❌ Standards Audit: ${standardsIssuesCount} issue(s)${colors.reset}`);
1810
- if (standardsIssues.cursorRuleset.length > 0) {
1811
- console.log(` - Cursor ruleset: ${standardsIssues.cursorRuleset.length}`);
1812
- }
1813
- if (standardsIssues.typescriptConfig.length > 0) {
1814
- console.log(` - TypeScript config: ${standardsIssues.typescriptConfig.length}`);
1815
- }
1816
- if (standardsIssues.anyTypes.length > 0) {
1817
- console.log(` - Any types: ${standardsIssues.anyTypes.length}`);
1818
- }
1819
- if (standardsIssues.namingConvention.length > 0) {
1820
- console.log(` - Naming conventions: ${standardsIssues.namingConvention.length}`);
1821
- }
1822
- if (standardsIssues.rlsPolicy.length > 0) {
1823
- console.log(` - RLS policies: ${standardsIssues.rlsPolicy.length}`);
1824
- }
1825
- if (standardsIssues.rpcNaming.length > 0) {
1826
- console.log(` - RPC naming: ${standardsIssues.rpcNaming.length}`);
1827
- }
1828
- if (standardsIssues.testingConfig.length > 0) {
1829
- console.log(` - Testing config: ${standardsIssues.testingConfig.length}`);
1830
- }
1831
- if (standardsIssues.inputValidation.length > 0) {
1832
- console.log(` - Input validation: ${standardsIssues.inputValidation.length}`);
1833
- }
1834
- if (standardsIssues.loggingSecurity.length > 0) {
1835
- console.log(` - Logging security: ${standardsIssues.loggingSecurity.length}`);
1836
- }
1837
- if (standardsIssues.errorMessages.length > 0) {
1838
- console.log(` - Error messages: ${standardsIssues.errorMessages.length}`);
1839
- }
1840
- }
1841
-
1842
- // RBAC Audit
1843
- if (rbacIssuesCount === 0) {
1844
- console.log(` ${colors.green}✅ RBAC Audit: 0 issues${colors.reset}`);
1845
- } else {
1846
- console.log(` ${colors.red}❌ RBAC Audit: ${rbacIssuesCount} issue(s)${colors.reset}`);
1847
- if (rbacIssues.rbacPageGuard.length > 0) {
1848
- console.log(` - PagePermissionGuard issues: ${rbacIssues.rbacPageGuard.length}`);
1849
- }
1850
- if (rbacIssues.rbacWrapperComponent.length > 0) {
1851
- console.log(` - Wrapper components: ${rbacIssues.rbacWrapperComponent.length}`);
1852
- }
1853
- if (rbacIssues.rbacWrapperFunction.length > 0) {
1854
- console.log(` - Wrapper functions: ${rbacIssues.rbacWrapperFunction.length}`);
1855
- }
1856
- if (rbacIssues.rbacResourceNames.length > 0) {
1857
- console.log(` - RESOURCE_NAMES constants: ${rbacIssues.rbacResourceNames.length}`);
1858
- }
1859
- if (rbacIssues.rbacAccessDenied.length > 0) {
1860
- console.log(` - AccessDenied component: ${rbacIssues.rbacAccessDenied.length}`);
1861
- }
1862
- if (rbacIssues.rbacDirectRPC.length > 0) {
1863
- console.log(` - Direct RBAC RPC calls: ${rbacIssues.rbacDirectRPC.length}`);
1864
- }
1865
- if (rbacIssues.rbacDirectTable.length > 0) {
1866
- console.log(` - Direct RBAC table queries: ${rbacIssues.rbacDirectTable.length}`);
1867
- }
1868
- if (rbacIssues.rbacHardcodedRole.length > 0) {
1869
- console.log(` - Hardcoded role checks: ${rbacIssues.rbacHardcodedRole.length}`);
1870
- }
1871
- if (rbacIssues.rbacEnforcePermissions.length > 0) {
1872
- console.log(` - enforcePermissions config: ${rbacIssues.rbacEnforcePermissions.length}`);
1873
- }
1874
- if (rbacIssues.rbacEdgeFunction.length > 0) {
1875
- console.log(` - Edge Functions RBAC: ${rbacIssues.rbacEdgeFunction.length}`);
1876
- }
1877
- }
1878
-
1879
- // Total summary
1880
- console.log(`\n${colors.bold}Total Issues: ${totalIssues === 0 ? colors.green + '0 (All checks passed!)' : colors.red + totalIssues}${colors.reset}\n`);
1881
-
1882
- // Generate and save markdown report
1883
- const markdownReport = generateMarkdownReport(auditResults);
1884
-
1885
- // Generate timestamp in yyyymmddHHMM format
1886
- const now = new Date();
1887
- const year = now.getFullYear();
1888
- const month = String(now.getMonth() + 1).padStart(2, '0');
1889
- const day = String(now.getDate()).padStart(2, '0');
1890
- const hours = String(now.getHours()).padStart(2, '0');
1891
- const minutes = String(now.getMinutes()).padStart(2, '0');
1892
- const timestamp = `${year}${month}${day}${hours}${minutes}`;
1893
-
1894
- // Helper function to add timestamp to filename
1895
- function addTimestampToFilename(filePath) {
1896
- const dir = path.dirname(filePath);
1897
- const ext = path.extname(filePath);
1898
- const name = path.basename(filePath, ext);
1899
- return path.join(dir, `${name}-${timestamp}${ext}`);
1900
- }
1901
-
1902
- // Determine report path
1903
- let reportPath;
1904
- if (outputPath) {
1905
- reportPath = addTimestampToFilename(path.join(consumingAppPath, outputPath));
1906
- } else {
1907
- // Default: save to audit/ directory
1908
- const auditDir = path.join(consumingAppPath, 'audit');
1909
- if (!fs.existsSync(auditDir)) {
1910
- fs.mkdirSync(auditDir, { recursive: true });
1911
- }
1912
- reportPath = path.join(auditDir, `pace-core-audit-${timestamp}.md`);
1913
- }
1914
-
1915
- // Save report
1916
- fs.writeFileSync(reportPath, markdownReport, 'utf8');
1917
-
1918
- // Display report location
1919
- const relativeReportPath = path.relative(consumingAppPath, reportPath);
1920
- console.log(`${colors.bold}Report saved to:${colors.reset} ${colors.cyan}${relativeReportPath}${colors.reset}\n`);
1921
-
1922
- // Exit with error code if issues found
1923
- process.exit(totalIssues > 0 ? 1 : 0);
1924
- }
1925
-
1926
- main();
1927
-