@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,355 @@
1
+ /**
2
+ * Standard 7: API & Tech Stack Audit
3
+ * @package @jmruthers/pace-core
4
+ * @module Audit/Standard7
5
+ *
6
+ * Audits consuming apps for compliance with Standard 7: API & Tech Stack.
7
+ * Validates RPC naming in SQL migrations, tech stack versions, and Vite configuration.
8
+ *
9
+ * Reference: packages/core/docs/standards/7-api-tech-stack-standards.md
10
+ */
11
+
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+ const { findSQLFiles, findConfigFiles, readFileSafe, getRelativePath, findPaceCorePackageJson } = require('../utils/file-utils.cjs');
15
+ const { getLineNumber, getCodeSnippet, isInCommentOrStringSQL } = require('../utils/code-utils.cjs');
16
+
17
+ /**
18
+ * Check RPC naming in SQL migrations (data_* for reads, app_* for writes)
19
+ */
20
+ function checkRPCNaming(consumingAppPath) {
21
+ const issues = [];
22
+
23
+ // Find SQL migration files
24
+ const migrationsPath = path.join(consumingAppPath, 'supabase', 'migrations');
25
+ const altMigrationsPath = path.join(consumingAppPath, 'migrations');
26
+
27
+ const migrationsDir = fs.existsSync(migrationsPath) ? migrationsPath :
28
+ (fs.existsSync(altMigrationsPath) ? altMigrationsPath : null);
29
+
30
+ if (!migrationsDir) {
31
+ return issues; // No migrations directory, skip check
32
+ }
33
+
34
+ const sqlFiles = findSQLFiles(migrationsDir);
35
+
36
+ sqlFiles.forEach(filePath => {
37
+ try {
38
+ const content = readFileSafe(filePath);
39
+ if (!content) {
40
+ return;
41
+ }
42
+
43
+ const relativePath = getRelativePath(filePath, consumingAppPath);
44
+
45
+ // Find CREATE FUNCTION statements for RPCs
46
+ const functionPattern = /CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+(?:public\.)?["']?([^"'\s()]+)["']?\s*\(/gi;
47
+ let match;
48
+
49
+ while ((match = functionPattern.exec(content)) !== null) {
50
+ const functionName = match[1];
51
+ const functionStart = match.index;
52
+
53
+ if (isInCommentOrStringSQL(content, functionStart)) {
54
+ continue;
55
+ }
56
+
57
+ // Skip if it's a helper function (starts with check_, get_, is_)
58
+ if (/^(check_|get_|is_)/i.test(functionName)) {
59
+ continue;
60
+ }
61
+
62
+ // Check if it's a system function (starts with pg_ or other system prefixes)
63
+ if (/^(pg_|information_schema|current_|session_|set_|reset_|show_)/i.test(functionName)) {
64
+ continue;
65
+ }
66
+
67
+ // Check if it's an RPC (functions that are exposed as RPCs typically follow naming pattern)
68
+ // Skip if it's a helper function (doesn't follow data_* or app_* pattern)
69
+ if (!/^(data_|app_)/.test(functionName)) {
70
+ // Not an RPC, but should follow naming convention
71
+ issues.push({
72
+ type: 'rpcNaming',
73
+ file: relativePath,
74
+ line: getLineNumber(content, functionStart),
75
+ message: `RPC function '${functionName}' does not follow naming convention. Should start with 'data_' (read) or 'app_' (write) prefix.`,
76
+ code: getCodeSnippet(content, functionStart, 0, 100),
77
+ severity: 'error',
78
+ fix: `Rename function to follow pattern: data_${functionName} (for read) or app_${functionName} (for write)`,
79
+ });
80
+ continue;
81
+ }
82
+
83
+ // Check naming pattern: data_* for reads, app_* for writes
84
+ const readVerbs = ['list', 'get', 'read'];
85
+ const writeVerbs = ['create', 'update', 'delete', 'bulk_create', 'bulk_update'];
86
+ const validVerbs = [...readVerbs, ...writeVerbs];
87
+
88
+ const parts = functionName.split('_');
89
+ const verb = parts[parts.length - 1];
90
+
91
+ if (!validVerbs.includes(verb)) {
92
+ issues.push({
93
+ type: 'rpcNaming',
94
+ file: relativePath,
95
+ line: getLineNumber(content, functionStart),
96
+ message: `RPC '${functionName}' uses invalid verb '${verb}'. Use only: ${validVerbs.join(', ')}`,
97
+ code: getCodeSnippet(content, functionStart, 0, 100),
98
+ severity: 'error',
99
+ fix: `Rename RPC to use valid verb: ${functionName.replace(verb, validVerbs[0])}`,
100
+ });
101
+ continue;
102
+ }
103
+
104
+ // Check prefix-verb alignment
105
+ const hasDataPrefix = functionName.startsWith('data_');
106
+ const hasAppPrefix = functionName.startsWith('app_');
107
+ const isReadOperation = readVerbs.includes(verb);
108
+ const isWriteOperation = writeVerbs.includes(verb);
109
+
110
+ if (hasDataPrefix && isWriteOperation) {
111
+ issues.push({
112
+ type: 'rpcNaming',
113
+ file: relativePath,
114
+ line: getLineNumber(content, functionStart),
115
+ message: `RPC '${functionName}' has 'data_' prefix but uses write verb '${verb}'. Use 'app_' prefix for write operations.`,
116
+ code: getCodeSnippet(content, functionStart, 0, 100),
117
+ severity: 'error',
118
+ fix: `Rename to app_${functionName.replace('data_', '')}`,
119
+ });
120
+ }
121
+
122
+ if (hasAppPrefix && isReadOperation) {
123
+ issues.push({
124
+ type: 'rpcNaming',
125
+ file: relativePath,
126
+ line: getLineNumber(content, functionStart),
127
+ message: `RPC '${functionName}' has 'app_' prefix but uses read verb '${verb}'. Use 'data_' prefix for read operations.`,
128
+ code: getCodeSnippet(content, functionStart, 0, 100),
129
+ severity: 'error',
130
+ fix: `Rename to data_${functionName.replace('app_', '')}`,
131
+ });
132
+ }
133
+ }
134
+ } catch (error) {
135
+ // Skip files that can't be read
136
+ }
137
+ });
138
+
139
+ return issues;
140
+ }
141
+
142
+ /**
143
+ * Check tech stack versions
144
+ */
145
+ function checkTechStackVersions(consumingAppPath) {
146
+ const issues = [];
147
+
148
+ const packageJsonPath = path.join(consumingAppPath, 'package.json');
149
+ const packageJsonContent = readFileSafe(packageJsonPath);
150
+
151
+ if (!packageJsonContent) {
152
+ return issues;
153
+ }
154
+
155
+ let packageJson;
156
+ try {
157
+ packageJson = JSON.parse(packageJsonContent);
158
+ } catch (error) {
159
+ return issues;
160
+ }
161
+
162
+ const allDeps = {
163
+ ...(packageJson.dependencies || {}),
164
+ ...(packageJson.devDependencies || {}),
165
+ };
166
+
167
+ // Get pace-core package.json for peer dependencies
168
+ const paceCorePath = findPaceCorePackageJson(consumingAppPath);
169
+ if (!paceCorePath) {
170
+ return issues;
171
+ }
172
+
173
+ const paceCorePkg = JSON.parse(fs.readFileSync(paceCorePath, 'utf8'));
174
+ const peerDeps = paceCorePkg.peerDependencies || {};
175
+
176
+ // Check required peer dependencies
177
+ const requiredPeers = ['react', 'react-dom', 'react-router-dom'];
178
+
179
+ requiredPeers.forEach(dep => {
180
+ if (!allDeps[dep]) {
181
+ issues.push({
182
+ type: 'techStack',
183
+ file: 'package.json',
184
+ line: 1,
185
+ message: `Missing required dependency: ${dep}. Required version: ${peerDeps[dep] || 'latest'}`,
186
+ severity: 'error',
187
+ fix: `Install ${dep}: npm install ${dep}@${peerDeps[dep] || 'latest'}`,
188
+ });
189
+ }
190
+ });
191
+
192
+ // Check React 19+ requirement
193
+ if (allDeps.react) {
194
+ const reactVersion = allDeps.react.replace(/[\^~]/, '');
195
+ const reactMajor = parseInt(reactVersion.split('.')[0]);
196
+
197
+ if (reactMajor < 19) {
198
+ issues.push({
199
+ type: 'techStack',
200
+ file: 'package.json',
201
+ line: 1,
202
+ message: `React version ${reactVersion} is below required version 19+. pace-core requires React 19+.`,
203
+ severity: 'error',
204
+ fix: 'Upgrade React: npm install react@^19.0.0 react-dom@^19.0.0',
205
+ });
206
+ }
207
+ }
208
+
209
+ return issues;
210
+ }
211
+
212
+ /**
213
+ * Check Vite configuration (optimizeDeps, resolve.dedupe)
214
+ */
215
+ function checkViteConfig(consumingAppPath) {
216
+ const issues = [];
217
+
218
+ const viteConfigFiles = findConfigFiles(consumingAppPath, ['vite.config.ts', 'vite.config.js']);
219
+ const viteConfigPath = viteConfigFiles['vite.config.ts'] || viteConfigFiles['vite.config.js'];
220
+
221
+ if (!viteConfigPath) {
222
+ return issues; // No vite config, skip
223
+ }
224
+
225
+ const content = readFileSafe(viteConfigPath);
226
+ if (!content) {
227
+ return issues;
228
+ }
229
+
230
+ const relativePath = getRelativePath(viteConfigPath, consumingAppPath);
231
+
232
+ // Check for optimizeDeps.exclude (should exclude pace-core and react-router-dom)
233
+ const hasOptimizeDepsExclude = /optimizeDeps\s*:\s*\{[^}]*exclude/.test(content);
234
+ if (!hasOptimizeDepsExclude) {
235
+ issues.push({
236
+ type: 'viteConfig',
237
+ file: relativePath,
238
+ line: 1,
239
+ message: 'vite.config.ts missing optimizeDeps.exclude. Should exclude @jmruthers/pace-core and react-router-dom to prevent React context mismatches.',
240
+ severity: 'warning',
241
+ fix: 'Add: optimizeDeps: { exclude: [\'@jmruthers/pace-core\', \'react-router-dom\'] }',
242
+ });
243
+ } else {
244
+ // Check if pace-core is excluded
245
+ const excludesPaceCore = /exclude\s*:\s*\[[^\]]*['"]@jmruthers\/pace-core['"]/.test(content);
246
+ if (!excludesPaceCore) {
247
+ issues.push({
248
+ type: 'viteConfig',
249
+ file: relativePath,
250
+ line: 1,
251
+ message: 'vite.config.ts optimizeDeps.exclude should include @jmruthers/pace-core to prevent React context mismatches.',
252
+ severity: 'warning',
253
+ fix: 'Add \'@jmruthers/pace-core\' to optimizeDeps.exclude array',
254
+ });
255
+ }
256
+ }
257
+
258
+ // Check for resolve.dedupe (should dedupe React dependencies)
259
+ // Need to handle nested objects, so look for dedupe anywhere after resolve: {
260
+ // Match resolve: { ... and then look for dedupe before the matching closing brace
261
+ // Use a more flexible pattern that handles nested braces
262
+ const resolvePattern = /resolve\s*:\s*\{/;
263
+ const resolveMatch = content.match(resolvePattern);
264
+
265
+ let hasResolveDedupe = false;
266
+ if (resolveMatch) {
267
+ const resolveStart = resolveMatch.index + resolveMatch[0].length;
268
+ // Find the matching closing brace for the resolve object
269
+ let braceCount = 1;
270
+ let i = resolveStart;
271
+ let resolveEnd = -1;
272
+
273
+ while (i < content.length && braceCount > 0) {
274
+ if (content[i] === '{') braceCount++;
275
+ if (content[i] === '}') braceCount--;
276
+ if (braceCount === 0) {
277
+ resolveEnd = i;
278
+ break;
279
+ }
280
+ i++;
281
+ }
282
+
283
+ if (resolveEnd > 0) {
284
+ const resolveBody = content.substring(resolveStart, resolveEnd);
285
+ // Check if dedupe exists in the resolve object body
286
+ hasResolveDedupe = /dedupe\s*:/.test(resolveBody);
287
+
288
+ // Also verify it includes the required dependencies
289
+ if (hasResolveDedupe) {
290
+ const dedupePattern = /dedupe\s*:\s*\[([^\]]+)\]/;
291
+ const dedupeMatch = resolveBody.match(dedupePattern);
292
+ if (dedupeMatch) {
293
+ const dedupeArray = dedupeMatch[1];
294
+ const hasReact = /['"]react['"]/.test(dedupeArray);
295
+ const hasReactDom = /['"]react-dom['"]/.test(dedupeArray);
296
+ const hasReactRouter = /['"]react-router-dom['"]/.test(dedupeArray);
297
+
298
+ // If any required dependency is missing, flag it
299
+ if (!hasReact || !hasReactDom || !hasReactRouter) {
300
+ issues.push({
301
+ type: 'viteConfig',
302
+ file: relativePath,
303
+ line: 1,
304
+ message: 'vite.config.ts resolve.dedupe is missing required dependencies. Should include: react, react-dom, react-router-dom',
305
+ severity: 'warning',
306
+ fix: 'Update resolve.dedupe to include: [\'react\', \'react-dom\', \'react-router-dom\']',
307
+ });
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+ if (!hasResolveDedupe) {
315
+ issues.push({
316
+ type: 'viteConfig',
317
+ file: relativePath,
318
+ line: 1,
319
+ message: 'vite.config.ts missing resolve.dedupe. Should dedupe React dependencies to prevent context mismatches.',
320
+ severity: 'warning',
321
+ fix: 'Add: resolve: { dedupe: [\'react\', \'react-dom\', \'react-router-dom\'] }',
322
+ });
323
+ }
324
+
325
+ return issues;
326
+ }
327
+
328
+ /**
329
+ * Run audit for Standard 7: API & Tech Stack
330
+ * @param {string} consumingAppPath - Path to consuming app
331
+ * @returns {object} - Audit results with issues array
332
+ */
333
+ function runStandard7Audit(consumingAppPath) {
334
+ const issues = [];
335
+
336
+ try {
337
+ issues.push(...checkRPCNaming(consumingAppPath));
338
+ issues.push(...checkTechStackVersions(consumingAppPath));
339
+ issues.push(...checkViteConfig(consumingAppPath));
340
+ } catch (error) {
341
+ return {
342
+ standard: '07-api-tech-stack',
343
+ issues: [],
344
+ error: error.message,
345
+ };
346
+ }
347
+
348
+ return {
349
+ standard: '07-api-tech-stack',
350
+ issues,
351
+ error: null,
352
+ };
353
+ }
354
+
355
+ module.exports = { runStandard7Audit };
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Standard 8: Testing & Documentation Audit
3
+ * @package @jmruthers/pace-core
4
+ * @module Audit/Standard8
5
+ *
6
+ * Audits consuming apps for compliance with Standard 8: Testing & Documentation.
7
+ * Validates test structure, test timeout configuration, and testing tools.
8
+ *
9
+ * Reference: packages/core/docs/standards/8-testing-documentation-standards.md
10
+ */
11
+
12
+ const path = require('path');
13
+ const { findConfigFiles, readFileSafe, getRelativePath, findSourceFiles, directoryExists } = require('../utils/file-utils.cjs');
14
+
15
+ /**
16
+ * Check test timeout configuration
17
+ */
18
+ function checkTestTimeoutConfig(consumingAppPath) {
19
+ const issues = [];
20
+
21
+ // Check vitest.config.ts
22
+ const vitestConfigFiles = findConfigFiles(consumingAppPath, ['vitest.config.ts', 'vitest.config.js']);
23
+ const vitestConfigPath = vitestConfigFiles['vitest.config.ts'] || vitestConfigFiles['vitest.config.js'];
24
+
25
+ // Also check package.json for test scripts
26
+ const packageJsonPath = path.join(consumingAppPath, 'package.json');
27
+ const packageJsonContent = readFileSafe(packageJsonPath);
28
+
29
+ let hasTimeoutInConfig = false;
30
+ let hasTimeoutInScripts = false;
31
+
32
+ // Check vitest.config.ts
33
+ if (vitestConfigPath) {
34
+ const content = readFileSafe(vitestConfigPath);
35
+ if (content) {
36
+ const hasTestTimeout = /testTimeout\s*[:=]/.test(content);
37
+ const hasHookTimeout = /hookTimeout\s*[:=]/.test(content);
38
+
39
+ if (hasTestTimeout || hasHookTimeout) {
40
+ hasTimeoutInConfig = true;
41
+ } else {
42
+ const relativePath = getRelativePath(vitestConfigPath, consumingAppPath);
43
+ issues.push({
44
+ type: 'testTimeout',
45
+ file: relativePath,
46
+ line: 1,
47
+ message: 'vitest.config.ts missing test timeout configuration. Tests may hang indefinitely without timeouts.',
48
+ severity: 'error',
49
+ fix: 'Add: test: { testTimeout: 10000, hookTimeout: 10000, teardownTimeout: 5000 }',
50
+ });
51
+ }
52
+ }
53
+ }
54
+
55
+ // Check package.json scripts
56
+ if (packageJsonContent) {
57
+ try {
58
+ const packageJson = JSON.parse(packageJsonContent);
59
+ const scripts = packageJson.scripts || {};
60
+
61
+ const testScripts = Object.values(scripts).filter(script =>
62
+ typeof script === 'string' && script.includes('vitest')
63
+ );
64
+
65
+ hasTimeoutInScripts = testScripts.some(script =>
66
+ script.includes('--test-timeout') || script.includes('testTimeout')
67
+ );
68
+
69
+ if (!hasTimeoutInConfig && !hasTimeoutInScripts && testScripts.length > 0) {
70
+ issues.push({
71
+ type: 'testTimeout',
72
+ file: 'package.json',
73
+ line: 1,
74
+ message: 'Test scripts missing timeout configuration. Tests may hang indefinitely without timeouts.',
75
+ severity: 'error',
76
+ fix: 'Add --test-timeout=10000 to test scripts or configure in vitest.config.ts',
77
+ });
78
+ }
79
+ } catch (error) {
80
+ // Skip if package.json can't be parsed
81
+ }
82
+ }
83
+
84
+ return issues;
85
+ }
86
+
87
+ /**
88
+ * Check testing tools (React Testing Library, userEvent, Vitest)
89
+ */
90
+ function checkTestingTools(consumingAppPath) {
91
+ const issues = [];
92
+
93
+ const packageJsonPath = path.join(consumingAppPath, 'package.json');
94
+ const packageJsonContent = readFileSafe(packageJsonPath);
95
+
96
+ if (!packageJsonContent) {
97
+ return issues;
98
+ }
99
+
100
+ let packageJson;
101
+ try {
102
+ packageJson = JSON.parse(packageJsonContent);
103
+ } catch (error) {
104
+ return issues;
105
+ }
106
+
107
+ const allDeps = {
108
+ ...(packageJson.dependencies || {}),
109
+ ...(packageJson.devDependencies || {}),
110
+ };
111
+
112
+ // Check for required testing tools
113
+ const requiredTools = [
114
+ { name: 'vitest', description: 'Test runner' },
115
+ { name: '@testing-library/react', description: 'React Testing Library' },
116
+ { name: '@testing-library/user-event', description: 'userEvent for interaction simulation' },
117
+ ];
118
+
119
+ requiredTools.forEach(({ name, description }) => {
120
+ if (!allDeps[name]) {
121
+ issues.push({
122
+ type: 'testingTools',
123
+ file: 'package.json',
124
+ line: 1,
125
+ message: `Missing required testing tool: ${name} (${description})`,
126
+ severity: 'warning',
127
+ fix: `Install: npm install --save-dev ${name}`,
128
+ });
129
+ }
130
+ });
131
+
132
+ return issues;
133
+ }
134
+
135
+ /**
136
+ * Check test file structure (colocation, naming)
137
+ */
138
+ function checkTestStructure(consumingAppPath) {
139
+ const issues = [];
140
+
141
+ const srcDir = path.join(consumingAppPath, 'src');
142
+ if (!directoryExists(srcDir)) {
143
+ return issues;
144
+ }
145
+
146
+ // Find all test files
147
+ const testFiles = findSourceFiles(srcDir).filter(file =>
148
+ file.includes('.test.') || file.includes('.spec.')
149
+ );
150
+
151
+ testFiles.forEach(testFile => {
152
+ const content = readFileSafe(testFile);
153
+ if (!content) {
154
+ return;
155
+ }
156
+
157
+ const relativePath = getRelativePath(testFile, consumingAppPath);
158
+
159
+ // Check if using .spec instead of .test
160
+ if (testFile.includes('.spec.')) {
161
+ issues.push({
162
+ type: 'testStructure',
163
+ file: relativePath,
164
+ line: 1,
165
+ message: 'Test file uses .spec extension. Should use .test extension for consistency.',
166
+ severity: 'warning',
167
+ fix: `Rename to ${testFile.replace('.spec.', '.test.')}`,
168
+ });
169
+ }
170
+ });
171
+
172
+ return issues;
173
+ }
174
+
175
+ /**
176
+ * Run audit for Standard 8: Testing & Documentation
177
+ * @param {string} consumingAppPath - Path to consuming app
178
+ * @returns {object} - Audit results with issues array
179
+ */
180
+ function runStandard8Audit(consumingAppPath) {
181
+ const issues = [];
182
+
183
+ try {
184
+ issues.push(...checkTestTimeoutConfig(consumingAppPath));
185
+ issues.push(...checkTestingTools(consumingAppPath));
186
+ issues.push(...checkTestStructure(consumingAppPath));
187
+ } catch (error) {
188
+ return {
189
+ standard: '08-testing-documentation',
190
+ issues: [],
191
+ error: error.message,
192
+ };
193
+ }
194
+
195
+ return {
196
+ standard: '08-testing-documentation',
197
+ issues,
198
+ error: null,
199
+ };
200
+ }
201
+
202
+ module.exports = { runStandard8Audit };