@jmruthers/pace-core 0.6.6 → 0.6.8

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 (292) hide show
  1. package/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +227 -22
  2. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  3. package/audit-tool/audits/02-project-structure.cjs +240 -0
  4. package/audit-tool/audits/03-architecture.cjs +224 -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 +554 -0
  8. package/audit-tool/audits/07-api-tech-stack.cjs +355 -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 +295 -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 +380 -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-6RMSCQJ6.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-CIGSujI2.d.ts} +40 -24
  28. package/dist/{UnifiedAuthProvider-ZT6TIGM7.js → UnifiedAuthProvider-7SNDOWYD.js} +2 -2
  29. package/dist/{api-Y4MQWOFW.js → api-7P7DI652.js} +1 -1
  30. package/dist/{chunk-MAGBIDNS.js → chunk-4DDCYDQ3.js} +8 -7
  31. package/dist/{chunk-BVP2BCJF.js → chunk-5W2A3DRC.js} +10 -9
  32. package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
  33. package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
  34. package/dist/{chunk-3O3WHILE.js → chunk-EF2UGZWY.js} +239 -63
  35. package/dist/{chunk-LAZMKTTF.js → chunk-EURB7QFZ.js} +341 -337
  36. package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
  37. package/dist/{chunk-7TYHROIV.js → chunk-GS5672WG.js} +55 -13
  38. package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
  39. package/dist/{chunk-ZFYPMX46.js → chunk-LX6U42O3.js} +1 -1
  40. package/dist/{chunk-FENMYN2U.js → chunk-MPBLMWVR.js} +3 -3
  41. package/dist/{chunk-ZS5VO5JB.js → chunk-NKHKXPI4.js} +408 -453
  42. package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
  43. package/dist/{chunk-4T7OBVTU.js → chunk-S6ZQKDY6.js} +1 -1
  44. package/dist/{chunk-FTCRZOG2.js → chunk-T5CVK4R3.js} +5 -5
  45. package/dist/{chunk-OHIK3MIO.js → chunk-Z2FNRKF3.js} +13 -13
  46. package/dist/components.d.ts +5 -4
  47. package/dist/components.js +29 -34
  48. package/dist/eslint-rules/index.cjs +22 -9
  49. package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
  50. package/dist/eslint-rules/rules/04-code-quality.cjs +346 -0
  51. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  52. package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +34 -13
  53. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +385 -0
  54. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  55. package/dist/{functions-DHebl8-F.d.ts → functions-lBy5L2ry.d.ts} +1 -1
  56. package/dist/hooks.d.ts +5 -5
  57. package/dist/hooks.js +8 -8
  58. package/dist/index.d.ts +7 -7
  59. package/dist/index.js +21 -20
  60. package/dist/providers.js +2 -2
  61. package/dist/rbac/index.d.ts +1 -1
  62. package/dist/rbac/index.js +8 -8
  63. package/dist/theming/runtime.d.ts +61 -1
  64. package/dist/theming/runtime.js +1 -1
  65. package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
  66. package/dist/types.d.ts +2 -2
  67. package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
  68. package/dist/utils.d.ts +2 -2
  69. package/dist/utils.js +8 -8
  70. package/docs/README.md +1 -1
  71. package/docs/api/modules.md +106 -41
  72. package/docs/api-reference/components.md +18 -20
  73. package/docs/api-reference/hooks.md +80 -80
  74. package/docs/api-reference/types.md +1 -1
  75. package/docs/api-reference/utilities.md +1 -1
  76. package/docs/architecture/README.md +1 -1
  77. package/docs/core-concepts/events.md +3 -3
  78. package/docs/core-concepts/organisations.md +6 -6
  79. package/docs/core-concepts/permissions.md +6 -6
  80. package/docs/documentation-index.md +12 -18
  81. package/docs/getting-started/dependencies.md +23 -0
  82. package/docs/getting-started/documentation-index.md +1 -1
  83. package/docs/getting-started/examples/README.md +4 -4
  84. package/docs/getting-started/examples/full-featured-app.md +1 -1
  85. package/docs/getting-started/faq.md +2 -2
  86. package/docs/getting-started/quick-reference.md +4 -4
  87. package/docs/implementation-guides/app-layout.md +1 -1
  88. package/docs/implementation-guides/authentication.md +15 -15
  89. package/docs/implementation-guides/component-styling.md +1 -1
  90. package/docs/implementation-guides/data-tables.md +127 -34
  91. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  92. package/docs/implementation-guides/dynamic-colors.md +3 -3
  93. package/docs/implementation-guides/file-upload-storage.md +2 -2
  94. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  95. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  96. package/docs/implementation-guides/large-datasets.md +3 -2
  97. package/docs/implementation-guides/organisation-security.md +2 -2
  98. package/docs/implementation-guides/performance.md +2 -2
  99. package/docs/implementation-guides/permission-enforcement.md +1 -1
  100. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  101. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  102. package/docs/rbac/README.md +5 -5
  103. package/docs/rbac/advanced-patterns.md +6 -6
  104. package/docs/rbac/api-reference.md +20 -20
  105. package/docs/rbac/event-based-apps.md +3 -3
  106. package/docs/rbac/examples.md +41 -41
  107. package/docs/rbac/getting-started.md +37 -37
  108. package/docs/rbac/performance.md +1 -1
  109. package/docs/rbac/quick-start.md +52 -52
  110. package/docs/rbac/secure-client-protection.md +1 -1
  111. package/docs/rbac/troubleshooting.md +1 -1
  112. package/docs/security/README.md +5 -5
  113. package/docs/standards/0-standards-overview.md +220 -0
  114. package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +241 -185
  115. package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
  116. package/docs/standards/3-architecture-standards.md +606 -0
  117. package/docs/standards/4-code-quality-standards.md +728 -0
  118. package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
  119. package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
  120. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  121. package/docs/standards/8-testing-documentation-standards.md +401 -0
  122. package/docs/standards/9-operations-standards.md +1102 -0
  123. package/docs/standards/README.md +203 -104
  124. package/docs/troubleshooting/README.md +4 -4
  125. package/docs/troubleshooting/common-issues.md +2 -2
  126. package/docs/troubleshooting/debugging.md +9 -9
  127. package/docs/troubleshooting/migration.md +4 -4
  128. package/eslint-config-pace-core.cjs +50 -20
  129. package/package.json +50 -19
  130. package/scripts/eslint-audit.cjs +123 -0
  131. package/scripts/install-cursor-rules.cjs +11 -243
  132. package/scripts/install-eslint-config.cjs +349 -0
  133. package/scripts/validate-dependencies.cjs +248 -0
  134. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
  135. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  136. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +30 -18
  137. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  138. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  139. package/src/__tests__/templates/accessibility.test.template.tsx +10 -9
  140. package/src/__tests__/templates/component.test.template.tsx +18 -15
  141. package/src/components/AddressField/AddressField.tsx +26 -1
  142. package/src/components/Alert/Alert.test.tsx +86 -22
  143. package/src/components/Alert/Alert.tsx +19 -11
  144. package/src/components/Badge/Badge.tsx +1 -1
  145. package/src/components/Calendar/Calendar.tsx +201 -47
  146. package/src/components/Checkbox/Checkbox.test.tsx +2 -1
  147. package/src/components/ContextSelector/ContextSelector.tsx +108 -126
  148. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  149. package/src/components/DataTable/DataTable.tsx +1 -19
  150. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +6 -2
  151. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +21 -6
  152. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +3 -2
  153. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  154. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  155. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  156. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  157. package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
  158. package/src/components/DataTable/components/EditableRow.tsx +5 -7
  159. package/src/components/DataTable/components/EmptyState.tsx +11 -10
  160. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  161. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  162. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  163. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  164. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  165. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  166. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  167. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  168. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  169. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +45 -27
  170. package/src/components/DataTable/components/index.ts +2 -1
  171. package/src/components/DataTable/types.ts +0 -18
  172. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  173. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +1 -1
  174. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  175. package/src/components/DateTimeField/DateTimeField.tsx +7 -8
  176. package/src/components/Dialog/Dialog.test.tsx +1 -0
  177. package/src/components/Dialog/Dialog.tsx +25 -8
  178. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  179. package/src/components/FileUpload/FileUpload.test.tsx +45 -16
  180. package/src/components/FileUpload/FileUpload.tsx +141 -130
  181. package/src/components/NavigationMenu/NavigationMenu.test.tsx +48 -12
  182. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +9 -9
  183. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +30 -30
  184. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -4
  185. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +7 -1
  186. package/src/components/Progress/Progress.tsx +2 -4
  187. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  188. package/src/components/Select/Select.tsx +86 -77
  189. package/src/components/Select/types.ts +3 -0
  190. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  191. package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
  192. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +8 -5
  193. package/src/hooks/__tests__/useFileUrl.unit.test.ts +4 -0
  194. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +99 -99
  195. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +45 -8
  196. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +22 -2
  197. package/src/hooks/public/usePublicEvent.ts +5 -5
  198. package/src/hooks/public/usePublicEventLogo.ts +5 -5
  199. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  200. package/src/hooks/public/usePublicRouteParams.ts +13 -9
  201. package/src/hooks/useAddressAutocomplete.test.ts +18 -18
  202. package/src/hooks/useAppConfig.ts +2 -2
  203. package/src/hooks/useEventTheme.test.ts +7 -7
  204. package/src/hooks/useEventTheme.ts +2 -1
  205. package/src/hooks/useFileDisplay.ts +2 -2
  206. package/src/hooks/useFileUrl.ts +52 -8
  207. package/src/hooks/useOrganisationSecurity.test.ts +2 -1
  208. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  209. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  210. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  211. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  212. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  213. package/src/providers/__tests__/ProviderLifecycle.test.tsx +38 -38
  214. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  215. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  216. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
  217. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +15 -6
  218. package/src/rbac/__tests__/rbac-functions.test.ts +3 -3
  219. package/src/rbac/api.test.ts +104 -0
  220. package/src/rbac/engine.ts +1 -1
  221. package/src/rbac/hooks/useCan.test.ts +2 -2
  222. package/src/rbac/secureClient.ts +1 -1
  223. package/src/rbac/types/functions.ts +1 -1
  224. package/src/styles/core.css +7 -0
  225. package/src/theming/__tests__/parseEventColours.test.ts +118 -3
  226. package/src/theming/parseEventColours.ts +77 -11
  227. package/src/types/supabase.ts +2 -3
  228. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +9 -9
  229. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  230. package/src/utils/file-reference/__tests__/file-reference.test.ts +4 -0
  231. package/src/utils/formatting/formatDate.test.ts +3 -2
  232. package/src/utils/formatting/formatDateTime.test.ts +2 -2
  233. package/src/utils/google-places/googlePlacesUtils.test.ts +36 -24
  234. package/src/utils/storage/README.md +1 -1
  235. package/src/utils/storage/__tests__/helpers.unit.test.ts +19 -12
  236. package/src/utils/storage/helpers.test.ts +69 -3
  237. package/cursor-rules/01-standards-compliance.mdc +0 -285
  238. package/cursor-rules/04-testing-standards.mdc +0 -270
  239. package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
  240. package/cursor-rules/06-code-quality.mdc +0 -311
  241. package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
  242. package/cursor-rules/10-error-handling-patterns.mdc +0 -179
  243. package/cursor-rules/11-performance-optimization.mdc +0 -169
  244. package/cursor-rules/12-ci-cd-integration.mdc +0 -150
  245. package/dist/DataTable-LRJL4IRV.js +0 -15
  246. package/dist/eslint-rules/rules/compliance.cjs +0 -348
  247. package/dist/eslint-rules/rules/components.cjs +0 -113
  248. package/dist/eslint-rules/rules/imports.cjs +0 -102
  249. package/docs/best-practices/README.md +0 -472
  250. package/docs/best-practices/accessibility.md +0 -604
  251. package/docs/best-practices/common-patterns.md +0 -516
  252. package/docs/best-practices/deployment.md +0 -1103
  253. package/docs/best-practices/performance.md +0 -1328
  254. package/docs/best-practices/security.md +0 -940
  255. package/docs/best-practices/testing.md +0 -1034
  256. package/docs/rbac/compliance/compliance-guide.md +0 -544
  257. package/docs/standards/01-standards-compliance.md +0 -188
  258. package/docs/standards/03-solid-principles.md +0 -39
  259. package/docs/standards/04-testing-standards.md +0 -36
  260. package/docs/standards/05-bug-reports-and-features.md +0 -27
  261. package/docs/standards/06-code-quality.md +0 -34
  262. package/docs/standards/07-tech-stack-compliance.md +0 -30
  263. package/docs/standards/10-error-handling-patterns.md +0 -401
  264. package/docs/standards/11-performance-optimization.md +0 -348
  265. package/docs/standards/12-ci-cd-integration.md +0 -370
  266. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
  267. package/scripts/audit/audit-compliance.cjs +0 -1295
  268. package/scripts/audit/audit-components.cjs +0 -260
  269. package/scripts/audit/audit-rbac.cjs +0 -954
  270. package/scripts/audit/audit-standards.cjs +0 -1268
  271. package/scripts/audit/index.cjs +0 -1927
  272. package/src/components/DataTable/components/DataTableBody.tsx +0 -478
  273. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  274. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  275. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  276. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  277. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  278. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  279. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  280. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  281. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  282. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  283. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  284. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  285. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  286. package/src/components/DataTable/utils/debugTools.ts +0 -514
  287. package/src/eslint-rules/index.cjs +0 -22
  288. package/src/eslint-rules/rules/components.cjs +0 -113
  289. package/src/eslint-rules/rules/imports.cjs +0 -102
  290. package/src/eslint-rules/rules/rbac.cjs +0 -790
  291. package/src/eslint-rules/utils/helpers.cjs +0 -42
  292. package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Standard 9: Operations Audit
3
+ * @package @jmruthers/pace-core
4
+ * @module Audit/Standard9
5
+ *
6
+ * Audits consuming apps for compliance with Standard 9: Operations.
7
+ * Validates error handling patterns, CI/CD configuration, and performance patterns.
8
+ *
9
+ * Reference: packages/core/docs/standards/9-operations-standards.md
10
+ */
11
+
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+ const { findSourceFiles, readFileSafe, getRelativePath, directoryExists } = require('../utils/file-utils.cjs');
15
+ const { getLineNumber, getCodeSnippet, isInCommentOrString } = require('../utils/code-utils.cjs');
16
+
17
+ /**
18
+ * Check error handling patterns (ApiResult usage)
19
+ */
20
+ function checkErrorHandlingPatterns(consumingAppPath) {
21
+ const issues = [];
22
+
23
+ const srcDir = path.join(consumingAppPath, 'src');
24
+ if (!directoryExists(srcDir)) {
25
+ return issues;
26
+ }
27
+
28
+ const sourceFiles = findSourceFiles(srcDir);
29
+
30
+ // Check for API functions that don't use ApiResult pattern
31
+ sourceFiles.forEach(filePath => {
32
+ const content = readFileSafe(filePath);
33
+ if (!content) {
34
+ return;
35
+ }
36
+
37
+ // Skip test files
38
+ if (filePath.includes('.test.') || filePath.includes('.spec.')) {
39
+ return;
40
+ }
41
+
42
+ const relativePath = getRelativePath(filePath, consumingAppPath);
43
+
44
+ // Look for async functions that might be API functions
45
+ const asyncFunctionPattern = /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\([^)]*\)\s*:\s*Promise/gi;
46
+ const matches = [...content.matchAll(asyncFunctionPattern)];
47
+
48
+ matches.forEach(match => {
49
+ const functionName = match[1];
50
+ const functionIndex = match.index;
51
+
52
+ // Skip if it's a hook
53
+ if (functionName.startsWith('use')) {
54
+ return;
55
+ }
56
+
57
+ // Check if function uses ApiResult
58
+ const functionBody = content.substring(functionIndex, Math.min(functionIndex + 500, content.length));
59
+ const usesApiResult = /ApiResult|ok:\s*(true|false)/.test(functionBody);
60
+
61
+ if (!usesApiResult) {
62
+ // Check if it's likely an API function (returns Promise, might throw errors)
63
+ const returnsPromise = /:\s*Promise/.test(match[0]);
64
+ const hasErrorHandling = /catch|throw|error/i.test(functionBody);
65
+
66
+ if (returnsPromise && hasErrorHandling) {
67
+ issues.push({
68
+ type: 'errorHandling',
69
+ file: relativePath,
70
+ line: getLineNumber(content, functionIndex),
71
+ message: `API function '${functionName}' does not use ApiResult pattern. Consider using ApiResult<T> for consistent error handling.`,
72
+ code: getCodeSnippet(content, functionIndex),
73
+ severity: 'info',
74
+ fix: 'Use ApiResult<T> type: type ApiResult<T> = { ok: true; data: T } | { ok: false; error: ApiError }',
75
+ });
76
+ }
77
+ }
78
+ });
79
+ });
80
+
81
+ return issues;
82
+ }
83
+
84
+ /**
85
+ * Check CI/CD configuration
86
+ */
87
+ function checkCICDConfig(consumingAppPath) {
88
+ const issues = [];
89
+
90
+ // Check for .github/workflows directory
91
+ const workflowsDir = path.join(consumingAppPath, '.github', 'workflows');
92
+
93
+ if (!directoryExists(workflowsDir)) {
94
+ issues.push({
95
+ type: 'cicd',
96
+ file: '.github/workflows/ (not found)',
97
+ line: 0,
98
+ message: 'CI/CD workflows directory not found. Consider setting up GitHub Actions for automated testing and deployment.',
99
+ severity: 'info',
100
+ fix: 'Create .github/workflows/ directory and add CI/CD workflow files',
101
+ });
102
+ return issues;
103
+ }
104
+
105
+ // Check for common workflow files
106
+ const workflowFiles = fs.existsSync(workflowsDir)
107
+ ? fs.readdirSync(workflowsDir).filter(file => file.endsWith('.yml') || file.endsWith('.yaml'))
108
+ : [];
109
+
110
+ if (workflowFiles.length === 0) {
111
+ issues.push({
112
+ type: 'cicd',
113
+ file: '.github/workflows/',
114
+ line: 0,
115
+ message: 'No CI/CD workflow files found. Consider setting up automated testing and deployment.',
116
+ severity: 'info',
117
+ fix: 'Create workflow files in .github/workflows/ (e.g., ci.yml, deploy.yml)',
118
+ });
119
+ }
120
+
121
+ return issues;
122
+ }
123
+
124
+ /**
125
+ * Check for error boundary usage
126
+ */
127
+ function checkErrorBoundaries(consumingAppPath) {
128
+ const issues = [];
129
+
130
+ const srcDir = path.join(consumingAppPath, 'src');
131
+ if (!directoryExists(srcDir)) {
132
+ return issues;
133
+ }
134
+
135
+ const sourceFiles = findSourceFiles(srcDir);
136
+
137
+ // Check if ErrorBoundary is used
138
+ let hasErrorBoundary = false;
139
+ sourceFiles.forEach(filePath => {
140
+ const content = readFileSafe(filePath);
141
+ if (!content) {
142
+ return;
143
+ }
144
+
145
+ if (/ErrorBoundary|errorBoundary/i.test(content)) {
146
+ hasErrorBoundary = true;
147
+ }
148
+ });
149
+
150
+ // Check main.tsx or App.tsx for ErrorBoundary
151
+ const mainFiles = [
152
+ path.join(consumingAppPath, 'src', 'main.tsx'),
153
+ path.join(consumingAppPath, 'src', 'App.tsx'),
154
+ path.join(consumingAppPath, 'App.tsx'),
155
+ ];
156
+
157
+ let hasErrorBoundaryInApp = false;
158
+ mainFiles.forEach(filePath => {
159
+ if (fs.existsSync(filePath)) {
160
+ const content = readFileSafe(filePath);
161
+ if (content && /ErrorBoundary|errorBoundary/i.test(content)) {
162
+ hasErrorBoundaryInApp = true;
163
+ }
164
+ }
165
+ });
166
+
167
+ if (!hasErrorBoundaryInApp && hasErrorBoundary) {
168
+ issues.push({
169
+ type: 'errorBoundary',
170
+ file: 'src/main.tsx or src/App.tsx',
171
+ line: 1,
172
+ message: 'ErrorBoundary component exists but is not used in main.tsx or App.tsx. Should wrap the app root to catch React errors.',
173
+ severity: 'warning',
174
+ fix: 'Wrap app root with ErrorBoundary in main.tsx or App.tsx',
175
+ });
176
+ }
177
+
178
+ return issues;
179
+ }
180
+
181
+ /**
182
+ * Run audit for Standard 9: Operations
183
+ * @param {string} consumingAppPath - Path to consuming app
184
+ * @returns {object} - Audit results with issues array
185
+ */
186
+ function runStandard9Audit(consumingAppPath) {
187
+ const issues = [];
188
+
189
+ try {
190
+ issues.push(...checkErrorHandlingPatterns(consumingAppPath));
191
+ issues.push(...checkCICDConfig(consumingAppPath));
192
+ issues.push(...checkErrorBoundaries(consumingAppPath));
193
+ } catch (error) {
194
+ return {
195
+ standard: '09-operations',
196
+ issues: [],
197
+ error: error.message,
198
+ };
199
+ }
200
+
201
+ return {
202
+ standard: '09-operations',
203
+ issues,
204
+ error: null,
205
+ };
206
+ }
207
+
208
+ module.exports = { runStandard9Audit };
@@ -0,0 +1,295 @@
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 dependency audit first
168
+ if (dependencyResult.error) {
169
+ console.log(` ${colors.red}❌ Dependency Audit: Error - ${dependencyResult.error}${colors.reset}`);
170
+ } else {
171
+ const depIssues = dependencyResult.issues || {};
172
+ const depCount = (depIssues.includedDeps?.length || 0) +
173
+ (depIssues.missingRequired?.length || 0) +
174
+ (depIssues.versionIssues?.length || 0) +
175
+ (depIssues.wrongLocation?.length || 0) +
176
+ (depIssues.missingDevDeps?.length || 0) +
177
+ (depIssues.devVersionIssues?.length || 0);
178
+
179
+ if (depCount === 0) {
180
+ console.log(` ${colors.green}✅ Dependency Audit: 0 issues${colors.reset}`);
181
+ } else {
182
+ console.log(` ${colors.red}❌ Dependency Audit: ${depCount} issue(s)${colors.reset}`);
183
+ }
184
+ }
185
+
186
+ // Display each standard
187
+ Object.entries(standardResults).forEach(([key, result]) => {
188
+ const standardNames = {
189
+ '01-pace-core-compliance': 'pace-core Compliance',
190
+ '02-project-structure': 'Project Structure',
191
+ '03-architecture': 'Architecture',
192
+ '04-code-quality': 'Code Quality',
193
+ '05-styling': 'Styling',
194
+ '06-security-rbac': 'Security & RBAC',
195
+ '07-api-tech-stack': 'API & Tech Stack',
196
+ '08-testing-documentation': 'Testing & Documentation',
197
+ '09-operations': 'Operations',
198
+ };
199
+
200
+ const name = standardNames[key] || key;
201
+ const issues = Array.isArray(result.issues) ? result.issues : Object.values(result.issues || {}).flat();
202
+ const count = issues.length;
203
+
204
+ if (count === 0) {
205
+ console.log(` ${colors.green}✅ ${name}: 0 issues${colors.reset}`);
206
+ } else {
207
+ console.log(` ${colors.red}❌ ${name}: ${count} issue(s)${colors.reset}`);
208
+ }
209
+ });
210
+
211
+ // Total summary
212
+ const totalIssues = summary.total + (dependencyResult.error ? 0 :
213
+ ((dependencyResult.issues?.includedDeps?.length || 0) +
214
+ (dependencyResult.issues?.missingRequired?.length || 0) +
215
+ (dependencyResult.issues?.versionIssues?.length || 0) +
216
+ (dependencyResult.issues?.wrongLocation?.length || 0) +
217
+ (dependencyResult.issues?.missingDevDeps?.length || 0) +
218
+ (dependencyResult.issues?.devVersionIssues?.length || 0)));
219
+
220
+ console.log(`\n${colors.bold}Total Issues: ${totalIssues === 0 ? colors.green + '0 (All checks passed!)' : colors.red + totalIssues}${colors.reset}\n`);
221
+
222
+ // Generate and save markdown report
223
+ const markdownReport = generateMarkdownReport(standardResults, consumingAppPath, dependencyResult);
224
+
225
+ // Generate timestamp in yyyymmddHHMM format
226
+ const now = new Date();
227
+ const year = now.getFullYear();
228
+ const month = String(now.getMonth() + 1).padStart(2, '0');
229
+ const day = String(now.getDate()).padStart(2, '0');
230
+ const hours = String(now.getHours()).padStart(2, '0');
231
+ const minutes = String(now.getMinutes()).padStart(2, '0');
232
+ const timestamp = `${year}${month}${day}${hours}${minutes}`;
233
+
234
+ // Helper function to add timestamp to filename
235
+ function addTimestampToFilename(filePath) {
236
+ const dir = path.dirname(filePath);
237
+ const ext = path.extname(filePath);
238
+ const name = path.basename(filePath, ext);
239
+ return path.join(dir, `${name}-${timestamp}${ext}`);
240
+ }
241
+
242
+ // Determine report path
243
+ let reportPath;
244
+ if (outputPath) {
245
+ reportPath = addTimestampToFilename(path.join(consumingAppPath, outputPath));
246
+ } else {
247
+ // Default: save to audit/ directory
248
+ const auditDir = path.join(consumingAppPath, 'audit');
249
+ if (!fs.existsSync(auditDir)) {
250
+ fs.mkdirSync(auditDir, { recursive: true });
251
+ }
252
+ reportPath = path.join(auditDir, `pace-core-audit-${timestamp}.md`);
253
+ }
254
+
255
+ // Save report
256
+ fs.writeFileSync(reportPath, markdownReport, 'utf8');
257
+
258
+ // Display report location
259
+ const relativeReportPath = path.relative(consumingAppPath, reportPath);
260
+ console.log(`${colors.bold}Report saved to:${colors.reset} ${colors.cyan}${relativeReportPath}${colors.reset}\n`);
261
+
262
+ // Exit with error code if issues found
263
+ process.exit(totalIssues > 0 ? 1 : 0);
264
+ }
265
+
266
+ // Export for programmatic usage
267
+ module.exports = {
268
+ runAudit: main,
269
+ runAllAudits,
270
+ runStandardAudit: (standardNumber, consumingAppPath) => {
271
+ const auditFunctions = {
272
+ '1': runStandard1Audit,
273
+ '2': runStandard2Audit,
274
+ '3': runStandard3Audit,
275
+ '4': runStandard4Audit,
276
+ '5': runStandard5Audit,
277
+ '6': runStandard6Audit,
278
+ '7': runStandard7Audit,
279
+ '8': runStandard8Audit,
280
+ '9': runStandard9Audit,
281
+ };
282
+
283
+ const fn = auditFunctions[standardNumber];
284
+ if (!fn) {
285
+ throw new Error(`Invalid standard number: ${standardNumber}. Must be 1-9.`);
286
+ }
287
+
288
+ return fn(consumingAppPath);
289
+ },
290
+ };
291
+
292
+ // Run if called directly
293
+ if (require.main === module) {
294
+ main();
295
+ }