@jmruthers/pace-core 0.6.4 → 0.6.6

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 (387) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +5 -403
  3. package/core-usage-manifest.json +93 -0
  4. package/cursor-rules/00-pace-core-compliance.mdc +128 -26
  5. package/cursor-rules/01-standards-compliance.mdc +49 -8
  6. package/cursor-rules/02-project-structure.mdc +6 -0
  7. package/cursor-rules/03-solid-principles.mdc +2 -0
  8. package/cursor-rules/04-testing-standards.mdc +2 -0
  9. package/cursor-rules/05-bug-reports-and-features.mdc +2 -0
  10. package/cursor-rules/06-code-quality.mdc +2 -0
  11. package/cursor-rules/07-tech-stack-compliance.mdc +2 -0
  12. package/cursor-rules/08-markup-quality.mdc +52 -27
  13. package/cursor-rules/09-rbac-compliance.mdc +462 -0
  14. package/cursor-rules/10-error-handling-patterns.mdc +179 -0
  15. package/cursor-rules/11-performance-optimization.mdc +169 -0
  16. package/cursor-rules/12-ci-cd-integration.mdc +150 -0
  17. package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
  18. package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-2N_tqbfq.d.ts} +1 -1
  19. package/dist/DataTable-LRJL4IRV.js +15 -0
  20. package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-BBH6Vqg7.d.ts} +72 -139
  21. package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
  22. package/dist/api-Y4MQWOFW.js +4 -0
  23. package/dist/audit-MYQXYZFU.js +3 -0
  24. package/dist/{chunk-J36DSWQK.js → chunk-2HGJFNAH.js} +8 -28
  25. package/dist/{chunk-OEWDTMG7.js → chunk-3O3WHILE.js} +38 -121
  26. package/dist/{chunk-M43Y4SSO.js → chunk-3QC3KRHK.js} +1 -14
  27. package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
  28. package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
  29. package/dist/chunk-4T7OBVTU.js +62 -0
  30. package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
  31. package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
  32. package/dist/{chunk-NN6WWZ5U.js → chunk-7TYHROIV.js} +579 -563
  33. package/dist/{chunk-M7MPQISP.js → chunk-A55DK444.js} +9 -16
  34. package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
  35. package/dist/{chunk-L4OXEN46.js → chunk-BVP2BCJF.js} +2 -16
  36. package/dist/chunk-C7NSAPTL.js +1 -0
  37. package/dist/{chunk-YKRAFF5K.js → chunk-FENMYN2U.js} +73 -149
  38. package/dist/{chunk-AVMLPIM7.js → chunk-FTCRZOG2.js} +284 -432
  39. package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
  40. package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
  41. package/dist/{chunk-I6DAQMWX.js → chunk-LAZMKTTF.js} +930 -891
  42. package/dist/{chunk-5EC5MEWX.js → chunk-MAGBIDNS.js} +77 -222
  43. package/dist/chunk-MBADTM7L.js +64 -0
  44. package/dist/chunk-OHIK3MIO.js +994 -0
  45. package/dist/{chunk-6SOIHG6Z.js → chunk-S7DKJPLT.js} +115 -44
  46. package/dist/{chunk-FMUCXFII.js → chunk-SD6WQY43.js} +1 -5
  47. package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
  48. package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
  49. package/dist/{chunk-FFQEQTNW.js → chunk-UIYSCEV7.js} +134 -45
  50. package/dist/{chunk-3LPHPB62.js → chunk-ZFYPMX46.js} +271 -87
  51. package/dist/{chunk-7JPAB3T5.js → chunk-ZS5VO5JB.js} +1989 -1283
  52. package/dist/components.d.ts +6 -6
  53. package/dist/components.js +57 -267
  54. package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
  55. package/dist/eslint-rules/index.cjs +22 -0
  56. package/dist/eslint-rules/rules/compliance.cjs +348 -0
  57. package/dist/eslint-rules/rules/components.cjs +113 -0
  58. package/dist/eslint-rules/rules/imports.cjs +102 -0
  59. package/dist/eslint-rules/rules/rbac.cjs +790 -0
  60. package/dist/eslint-rules/utils/helpers.cjs +42 -0
  61. package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
  62. package/dist/hooks.d.ts +5 -5
  63. package/dist/hooks.js +62 -270
  64. package/dist/icons/index.d.ts +1 -0
  65. package/dist/icons/index.js +1 -0
  66. package/dist/index.d.ts +36 -26
  67. package/dist/index.js +87 -690
  68. package/dist/providers.d.ts +2 -2
  69. package/dist/providers.js +8 -35
  70. package/dist/rbac/eslint-rules.d.ts +46 -44
  71. package/dist/rbac/eslint-rules.js +7 -4
  72. package/dist/rbac/index.d.ts +124 -594
  73. package/dist/rbac/index.js +14 -207
  74. package/dist/styles/index.js +2 -12
  75. package/dist/theming/runtime.js +3 -19
  76. package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
  77. package/dist/{types-CkbwOr4Y.d.ts → types-B-K_5VnO.d.ts} +4 -0
  78. package/dist/types-t9H8qKRw.d.ts +55 -0
  79. package/dist/types.d.ts +1 -1
  80. package/dist/types.js +7 -94
  81. package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-COZ28Mvq.d.ts} +9 -9
  82. package/dist/utils.d.ts +24 -117
  83. package/dist/utils.js +54 -392
  84. package/docs/README.md +16 -6
  85. package/docs/api/README.md +4 -402
  86. package/docs/api/modules.md +454 -930
  87. package/docs/api-reference/components.md +3 -1
  88. package/docs/api-reference/deprecated.md +31 -6
  89. package/docs/api-reference/rpc-functions.md +78 -3
  90. package/docs/best-practices/accessibility.md +6 -3
  91. package/docs/getting-started/cursor-rules.md +3 -23
  92. package/docs/getting-started/dependencies.md +650 -0
  93. package/docs/getting-started/installation-guide.md +20 -7
  94. package/docs/getting-started/quick-start.md +23 -12
  95. package/docs/implementation-guides/permission-enforcement.md +4 -0
  96. package/docs/rbac/MIGRATION_GUIDE.md +819 -0
  97. package/docs/rbac/RBAC_CONTRACT.md +724 -0
  98. package/docs/rbac/README.md +12 -3
  99. package/docs/rbac/edge-functions-guide.md +376 -0
  100. package/docs/rbac/secure-client-protection.md +0 -34
  101. package/docs/standards/00-pace-core-compliance.md +967 -0
  102. package/docs/standards/01-standards-compliance.md +188 -0
  103. package/docs/standards/02-project-structure.md +985 -0
  104. package/docs/standards/03-solid-principles.md +39 -0
  105. package/docs/standards/04-testing-standards.md +36 -0
  106. package/docs/standards/05-bug-reports-and-features.md +27 -0
  107. package/docs/standards/{04-code-style-standard.md → 06-code-quality.md} +2 -0
  108. package/docs/standards/07-tech-stack-compliance.md +30 -0
  109. package/docs/standards/08-markup-quality.md +345 -0
  110. package/docs/standards/{07-rbac-and-rls-standard.md → 09-rbac-compliance.md} +149 -54
  111. package/docs/standards/10-error-handling-patterns.md +401 -0
  112. package/docs/standards/11-performance-optimization.md +348 -0
  113. package/docs/standards/12-ci-cd-integration.md +370 -0
  114. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +192 -0
  115. package/docs/standards/README.md +62 -33
  116. package/docs/troubleshooting/organisation-context-setup.md +42 -19
  117. package/eslint-config-pace-core.cjs +20 -4
  118. package/package.json +31 -21
  119. package/scripts/audit/audit-compliance.cjs +1295 -0
  120. package/scripts/audit/audit-components.cjs +260 -0
  121. package/scripts/audit/audit-dependencies.cjs +395 -0
  122. package/scripts/audit/audit-rbac.cjs +954 -0
  123. package/scripts/audit/audit-standards.cjs +1268 -0
  124. package/scripts/audit/index.cjs +1898 -194
  125. package/scripts/install-cursor-rules.cjs +259 -8
  126. package/scripts/validate-master.js +1 -1
  127. package/src/__tests__/fixtures/supabase.ts +1 -1
  128. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +1 -1
  129. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
  130. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
  131. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +3 -3
  132. package/src/__tests__/helpers/component-test-utils.tsx +1 -1
  133. package/src/__tests__/helpers/supabaseMock.ts +2 -2
  134. package/src/__tests__/public-recipe-view.test.ts +38 -9
  135. package/src/components/Button/Button.tsx +5 -1
  136. package/src/components/ContextSelector/ContextSelector.tsx +42 -39
  137. package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
  138. package/src/components/DataTable/components/DataTableBody.tsx +55 -31
  139. package/src/components/DataTable/components/DataTableCore.tsx +186 -13
  140. package/src/components/DataTable/components/DataTableLayout.tsx +30 -5
  141. package/src/components/DataTable/components/EditFields.tsx +23 -3
  142. package/src/components/DataTable/components/EditableRow.tsx +7 -2
  143. package/src/components/DataTable/components/ImportModal.tsx +4 -6
  144. package/src/components/DataTable/components/RowComponent.tsx +12 -0
  145. package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
  146. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
  147. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
  148. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
  149. package/src/components/DataTable/core/DataTableContext.tsx +1 -1
  150. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
  151. package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
  152. package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
  153. package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
  154. package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
  155. package/src/components/DataTable/types.ts +5 -0
  156. package/src/components/DateTimeField/DateTimeField.tsx +20 -20
  157. package/src/components/DateTimeField/README.md +5 -2
  158. package/src/components/Dialog/Dialog.test.tsx +361 -318
  159. package/src/components/Dialog/Dialog.tsx +1154 -323
  160. package/src/components/Dialog/index.ts +3 -3
  161. package/src/components/FileDisplay/FileDisplay.test.tsx +45 -2
  162. package/src/components/FileDisplay/FileDisplay.tsx +28 -22
  163. package/src/components/Form/Form.test.tsx +9 -10
  164. package/src/components/Form/Form.tsx +369 -9
  165. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
  166. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
  167. package/src/components/LoginForm/LoginForm.tsx +2 -2
  168. package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
  169. package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
  170. package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
  171. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
  172. package/src/components/PaceAppLayout/PaceAppLayout.tsx +30 -41
  173. package/src/components/PaceAppLayout/README.md +10 -9
  174. package/src/components/PaceAppLayout/test-setup.tsx +40 -31
  175. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
  176. package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
  177. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
  178. package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
  179. package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
  180. package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
  181. package/src/components/Select/Select.tsx +23 -21
  182. package/src/components/Select/types.ts +1 -1
  183. package/src/components/UserMenu/UserMenu.test.tsx +38 -6
  184. package/src/components/UserMenu/UserMenu.tsx +39 -34
  185. package/src/components/index.ts +3 -4
  186. package/src/eslint-rules/index.cjs +22 -0
  187. package/src/eslint-rules/rules/compliance.cjs +348 -0
  188. package/src/eslint-rules/rules/components.cjs +113 -0
  189. package/src/eslint-rules/rules/imports.cjs +102 -0
  190. package/src/eslint-rules/rules/rbac.cjs +790 -0
  191. package/src/eslint-rules/utils/helpers.cjs +42 -0
  192. package/src/eslint-rules/utils/manifest-loader.cjs +75 -0
  193. package/src/hooks/__tests__/hooks.integration.test.tsx +6 -8
  194. package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
  195. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
  196. package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
  197. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
  198. package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
  199. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
  200. package/src/hooks/public/usePublicEvent.ts +62 -190
  201. package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
  202. package/src/hooks/public/usePublicEventLogo.ts +19 -9
  203. package/src/hooks/useAppConfig.ts +26 -24
  204. package/src/hooks/useEventTheme.test.ts +211 -233
  205. package/src/hooks/useEventTheme.ts +19 -28
  206. package/src/hooks/useEvents.ts +11 -7
  207. package/src/hooks/useKeyboardShortcuts.ts +1 -1
  208. package/src/hooks/useOrganisationPermissions.ts +9 -11
  209. package/src/hooks/useOrganisations.ts +13 -7
  210. package/src/hooks/useQueryCache.ts +0 -1
  211. package/src/hooks/useSessionDraft.ts +380 -0
  212. package/src/hooks/useSessionRestoration.ts +3 -1
  213. package/src/icons/index.ts +27 -0
  214. package/src/index.ts +16 -1
  215. package/src/providers/OrganisationProvider.tsx +23 -14
  216. package/src/providers/services/EventServiceProvider.tsx +1 -24
  217. package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
  218. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -0
  219. package/src/rbac/README.md +20 -20
  220. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
  221. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
  222. package/src/rbac/adapters.tsx +7 -295
  223. package/src/rbac/api.test.ts +44 -56
  224. package/src/rbac/api.ts +10 -17
  225. package/src/rbac/cache-invalidation.ts +0 -1
  226. package/src/rbac/compliance/index.ts +10 -0
  227. package/src/rbac/compliance/pattern-detector.ts +553 -0
  228. package/src/rbac/compliance/runtime-compliance.ts +22 -0
  229. package/src/rbac/components/AccessDenied.tsx +150 -0
  230. package/src/rbac/components/NavigationGuard.tsx +12 -20
  231. package/src/rbac/components/PagePermissionGuard.tsx +4 -24
  232. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
  233. package/src/rbac/components/index.ts +3 -41
  234. package/src/rbac/eslint-rules.js +1 -1
  235. package/src/rbac/hooks/index.ts +0 -3
  236. package/src/rbac/hooks/permissions/index.ts +0 -3
  237. package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
  238. package/src/rbac/hooks/usePermissions.ts +0 -3
  239. package/src/rbac/hooks/useRBAC.test.ts +21 -3
  240. package/src/rbac/hooks/useRBAC.ts +4 -3
  241. package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
  242. package/src/rbac/hooks/useResolvedScope.ts +58 -140
  243. package/src/rbac/hooks/useResourcePermissions.test.ts +241 -60
  244. package/src/rbac/hooks/useResourcePermissions.ts +182 -63
  245. package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
  246. package/src/rbac/hooks/useRoleManagement.ts +147 -19
  247. package/src/rbac/hooks/useSecureSupabase.ts +4 -8
  248. package/src/rbac/index.ts +7 -9
  249. package/src/rbac/permissions.ts +17 -17
  250. package/src/rbac/utils/contextValidator.ts +45 -7
  251. package/src/services/AuthService.ts +132 -23
  252. package/src/services/EventService.ts +4 -97
  253. package/src/services/InactivityService.ts +155 -58
  254. package/src/services/OrganisationService.ts +7 -44
  255. package/src/services/__tests__/OrganisationService.test.ts +26 -8
  256. package/src/services/base/BaseService.ts +0 -3
  257. package/src/styles/core.css +4 -0
  258. package/src/types/database.generated.ts +4733 -3809
  259. package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
  260. package/src/utils/context/organisationContext.test.ts +13 -28
  261. package/src/utils/context/organisationContext.ts +21 -52
  262. package/src/utils/dynamic/dynamicUtils.ts +1 -1
  263. package/src/utils/file-reference/index.ts +39 -15
  264. package/src/utils/formatting/formatDateTime.test.ts +3 -2
  265. package/src/utils/formatting/formatTime.test.ts +3 -2
  266. package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
  267. package/src/utils/index.ts +4 -1
  268. package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
  269. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
  270. package/src/utils/persistence/keyDerivation.ts +304 -0
  271. package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
  272. package/src/utils/security/secureStorage.ts +5 -5
  273. package/src/utils/storage/helpers.ts +3 -3
  274. package/src/utils/supabase/createBaseClient.ts +147 -0
  275. package/src/utils/timezone/timezone.test.ts +1 -2
  276. package/src/utils/timezone/timezone.ts +1 -1
  277. package/src/utils/validation/csrf.ts +4 -4
  278. package/cursor-rules/CHANGELOG.md +0 -119
  279. package/cursor-rules/README.md +0 -192
  280. package/dist/DataTable-E7YQZD7D.js +0 -175
  281. package/dist/DataTable-E7YQZD7D.js.map +0 -1
  282. package/dist/UnifiedAuthProvider-QPXO24B4.js +0 -18
  283. package/dist/UnifiedAuthProvider-QPXO24B4.js.map +0 -1
  284. package/dist/api-6LVZTHDS.js +0 -52
  285. package/dist/api-6LVZTHDS.js.map +0 -1
  286. package/dist/audit-V53FV5AG.js +0 -17
  287. package/dist/audit-V53FV5AG.js.map +0 -1
  288. package/dist/chunk-36LVWXB2.js +0 -227
  289. package/dist/chunk-36LVWXB2.js.map +0 -1
  290. package/dist/chunk-3LPHPB62.js.map +0 -1
  291. package/dist/chunk-5DRSZLL2.js.map +0 -1
  292. package/dist/chunk-5EC5MEWX.js.map +0 -1
  293. package/dist/chunk-63FOKYGO.js.map +0 -1
  294. package/dist/chunk-6SOIHG6Z.js.map +0 -1
  295. package/dist/chunk-7JPAB3T5.js.map +0 -1
  296. package/dist/chunk-ATKZM7RX.js +0 -2053
  297. package/dist/chunk-ATKZM7RX.js.map +0 -1
  298. package/dist/chunk-AVMLPIM7.js.map +0 -1
  299. package/dist/chunk-DGUM43GV.js.map +0 -1
  300. package/dist/chunk-E66EQZE6.js.map +0 -1
  301. package/dist/chunk-FFQEQTNW.js.map +0 -1
  302. package/dist/chunk-FMUCXFII.js.map +0 -1
  303. package/dist/chunk-G37KK66H.js.map +0 -1
  304. package/dist/chunk-I6DAQMWX.js.map +0 -1
  305. package/dist/chunk-J36DSWQK.js.map +0 -1
  306. package/dist/chunk-KQCRWDSA.js +0 -1
  307. package/dist/chunk-KQCRWDSA.js.map +0 -1
  308. package/dist/chunk-L4OXEN46.js.map +0 -1
  309. package/dist/chunk-LMC26NLJ.js +0 -84
  310. package/dist/chunk-LMC26NLJ.js.map +0 -1
  311. package/dist/chunk-M43Y4SSO.js.map +0 -1
  312. package/dist/chunk-M7MPQISP.js.map +0 -1
  313. package/dist/chunk-NN6WWZ5U.js.map +0 -1
  314. package/dist/chunk-OEWDTMG7.js.map +0 -1
  315. package/dist/chunk-PWLANIRT.js.map +0 -1
  316. package/dist/chunk-QXHPKYJV.js.map +0 -1
  317. package/dist/chunk-VBXEHIUJ.js.map +0 -1
  318. package/dist/chunk-YKRAFF5K.js.map +0 -1
  319. package/dist/chunk-ZSAAAMVR.js.map +0 -1
  320. package/dist/components.js.map +0 -1
  321. package/dist/contextValidator-OOPCLPZW.js +0 -9
  322. package/dist/contextValidator-OOPCLPZW.js.map +0 -1
  323. package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
  324. package/dist/hooks.js.map +0 -1
  325. package/dist/index.js.map +0 -1
  326. package/dist/providers.js.map +0 -1
  327. package/dist/rbac/eslint-rules.js.map +0 -1
  328. package/dist/rbac/index.js.map +0 -1
  329. package/dist/styles/index.js.map +0 -1
  330. package/dist/theming/runtime.js.map +0 -1
  331. package/dist/types.js.map +0 -1
  332. package/dist/utils.js.map +0 -1
  333. package/docs/standards/01-architecture-standard.md +0 -44
  334. package/docs/standards/02-api-and-rpc-standard.md +0 -39
  335. package/docs/standards/03-component-standard.md +0 -32
  336. package/docs/standards/05-security-standard.md +0 -44
  337. package/docs/standards/06-testing-and-docs-standard.md +0 -29
  338. package/docs/standards/pace-core-compliance.md +0 -432
  339. package/scripts/audit/core/checks/accessibility.cjs +0 -197
  340. package/scripts/audit/core/checks/api-usage.cjs +0 -191
  341. package/scripts/audit/core/checks/bundle.cjs +0 -142
  342. package/scripts/audit/core/checks/compliance.cjs +0 -2706
  343. package/scripts/audit/core/checks/config.cjs +0 -54
  344. package/scripts/audit/core/checks/coverage.cjs +0 -84
  345. package/scripts/audit/core/checks/dependencies.cjs +0 -994
  346. package/scripts/audit/core/checks/documentation.cjs +0 -268
  347. package/scripts/audit/core/checks/environment.cjs +0 -116
  348. package/scripts/audit/core/checks/error-handling.cjs +0 -340
  349. package/scripts/audit/core/checks/forms.cjs +0 -172
  350. package/scripts/audit/core/checks/heuristics.cjs +0 -68
  351. package/scripts/audit/core/checks/hooks.cjs +0 -334
  352. package/scripts/audit/core/checks/imports.cjs +0 -244
  353. package/scripts/audit/core/checks/performance.cjs +0 -325
  354. package/scripts/audit/core/checks/routes.cjs +0 -117
  355. package/scripts/audit/core/checks/state.cjs +0 -130
  356. package/scripts/audit/core/checks/structure.cjs +0 -65
  357. package/scripts/audit/core/checks/style.cjs +0 -584
  358. package/scripts/audit/core/checks/testing.cjs +0 -122
  359. package/scripts/audit/core/checks/typescript.cjs +0 -61
  360. package/scripts/audit/core/scanner.cjs +0 -199
  361. package/scripts/audit/core/utils.cjs +0 -137
  362. package/scripts/audit/reporters/console.cjs +0 -151
  363. package/scripts/audit/reporters/json.cjs +0 -54
  364. package/scripts/audit/reporters/markdown.cjs +0 -124
  365. package/scripts/audit-consuming-app.cjs +0 -86
  366. package/src/eslint-rules/pace-core-compliance.cjs +0 -510
  367. package/src/eslint-rules/pace-core-compliance.js +0 -638
  368. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
  369. package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
  370. package/src/rbac/components/NavigationProvider.test.tsx +0 -481
  371. package/src/rbac/components/NavigationProvider.tsx +0 -345
  372. package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
  373. package/src/rbac/components/PagePermissionProvider.tsx +0 -279
  374. package/src/rbac/components/PermissionEnforcer.tsx +0 -312
  375. package/src/rbac/components/RoleBasedRouter.tsx +0 -440
  376. package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
  377. package/src/rbac/components/SecureDataProvider.tsx +0 -339
  378. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
  379. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
  380. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
  381. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
  382. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
  383. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
  384. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
  385. package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
  386. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
  387. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
@@ -1,340 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Error Handling Check Module
5
- * @package @jmruthers/pace-core
6
- * @module Audit/Checks/ErrorHandling
7
- *
8
- * Checks for:
9
- * - Missing error boundaries
10
- * - Unhandled promise rejections
11
- * - Missing error handling in async functions
12
- * - Silent error swallowing
13
- * - Missing error states in components
14
- */
15
-
16
- const fs = require('fs');
17
- const path = require('path');
18
- const { getRelativePath, getLineNumber } = require('../utils.cjs');
19
-
20
- const errorHandlingCheck = {
21
- name: 'error-handling',
22
- description: 'Error handling patterns (error boundaries, async error handling)',
23
- severity: 'warning',
24
-
25
- async run(context) {
26
- const { projectRoot, files } = context;
27
- const issues = [];
28
- const warnings = [];
29
- const suggestions = [];
30
-
31
- if (!files || files.length === 0) {
32
- return { issues, warnings, suggestions };
33
- }
34
-
35
- // Skip if this is the pace-core repository itself
36
- // Detect pace-core repository by checking if packages/core exists
37
- const packagesCorePath = path.join(projectRoot, 'packages', 'core');
38
- const isPaceCoreRepository = fs.existsSync(packagesCorePath);
39
-
40
- // Check for ErrorBoundary usage in main app file
41
- let hasErrorBoundary = false;
42
- const mainFiles = ['main.tsx', 'main.ts', 'App.tsx', 'App.ts', 'index.tsx', 'index.ts'];
43
-
44
- // First, specifically check the main app file for ErrorBoundary usage
45
- // This must be done before the general file loop to ensure we check the actual main file
46
- // Previously, the check would skip src/ files (thinking they were demo apps) and only
47
- // check if ErrorBoundary appeared anywhere in the codebase. Now we specifically check
48
- // the main.tsx file for proper ErrorBoundary import and JSX usage patterns.
49
- if (!isPaceCoreRepository) {
50
- const mainFile = mainFiles.find(file => {
51
- const filePath = path.join(projectRoot, 'src', file);
52
- return fs.existsSync(filePath);
53
- });
54
-
55
- if (mainFile) {
56
- const mainFilePath = path.join(projectRoot, 'src', mainFile);
57
- try {
58
- const mainContent = fs.readFileSync(mainFilePath, 'utf8');
59
-
60
- // Check for ErrorBoundary import from pace-core
61
- // Matches: import { ErrorBoundary } from '@jmruthers/pace-core'
62
- // Matches: import { ..., ErrorBoundary, ... } from '@jmruthers/pace-core'
63
- // Matches: import { ErrorBoundary } from '@jmruthers/pace-core/components'
64
- const hasErrorBoundaryImport = /import\s+[^'"]*ErrorBoundary[^'"]*from\s+['"]@jmruthers\/pace-core/.test(mainContent);
65
-
66
- // Check for ErrorBoundary JSX usage
67
- // Matches: <ErrorBoundary ...> or <ErrorBoundary>
68
- const hasErrorBoundaryJSX = /<ErrorBoundary[\s>]/.test(mainContent);
69
-
70
- // Check if ErrorBoundary wraps the render call or app component
71
- // Pattern 1: createRoot(...).render(<ErrorBoundary ...)
72
- // Pattern 2: render(<ErrorBoundary ...)
73
- // Pattern 3: <ErrorBoundary ...> wrapping App component or createRoot call
74
- const hasErrorBoundaryInRender = /createRoot\s*\([^)]*\)\s*\.\s*render\s*\(\s*<ErrorBoundary/.test(mainContent) ||
75
- /\.render\s*\(\s*<ErrorBoundary/.test(mainContent) ||
76
- /<ErrorBoundary[\s\S]{0,500}?<App[\s>]/.test(mainContent) ||
77
- /<ErrorBoundary[\s\S]{0,500}?createRoot/.test(mainContent);
78
-
79
- // ErrorBoundary is present if it's imported AND used in JSX
80
- // We check both JSX usage and render wrapping to catch all valid patterns
81
- if (hasErrorBoundaryImport && (hasErrorBoundaryJSX || hasErrorBoundaryInRender)) {
82
- hasErrorBoundary = true;
83
- }
84
- } catch (error) {
85
- // If we can't read the main file, continue with other checks
86
- }
87
- }
88
- }
89
-
90
- for (const filePath of files) {
91
- try {
92
- const content = fs.readFileSync(filePath, 'utf8');
93
- const relativePath = getRelativePath(filePath, projectRoot);
94
- const normalizedPath = relativePath.replace(/\\/g, '/');
95
- const fileName = path.basename(relativePath);
96
-
97
- // Skip root-level src directory - in pace-core repository, this is a demo/showcase app
98
- // Note: We DO check packages/core/ files because error handling issues (missing try/catch, missing .catch()) are real issues that should be fixed
99
- const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
100
- if (isRootSrc) {
101
- continue; // Skip demo app files
102
- }
103
-
104
- // Skip scripts directory - utility scripts don't need error handling validation
105
- const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
106
- if (isScript) {
107
- continue; // Skip script files
108
- }
109
-
110
- // Check for unhandled promise rejections
111
- const asyncFunctionPattern = /(async\s+function|const\s+\w+\s*=\s*async|export\s+async\s+function)/g;
112
- let asyncMatch;
113
- while ((asyncMatch = asyncFunctionPattern.exec(content)) !== null) {
114
- const functionStart = asyncMatch.index;
115
- const beforeAsync = content.substring(Math.max(0, functionStart - 100), functionStart);
116
- const afterAsync = content.substring(functionStart, Math.min(content.length, functionStart + 1000));
117
-
118
- // Check if function has try/catch
119
- const hasTryCatch = /try\s*\{/.test(afterAsync);
120
-
121
- // Check for await calls
122
- const hasAwait = /await\s+/.test(afterAsync);
123
-
124
- // Check if this is an exported library function (designed to throw errors for callers to handle)
125
- // Look for "export" before the async function declaration
126
- const isExportedLibraryFunction = /export\s+(async\s+function|function)/.test(beforeAsync + asyncMatch[0]) ||
127
- asyncMatch[0].includes('export');
128
-
129
- // Skip pace-core files - library code has different error handling patterns
130
- // Exported functions are designed to throw errors for callers to handle
131
- // Internal utility functions may also intentionally propagate errors
132
- const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
133
-
134
- if (hasAwait && !hasTryCatch && !isExportedLibraryFunction && !isPaceCorePackage) {
135
- warnings.push({
136
- type: 'unhandled-async',
137
- file: relativePath,
138
- line: getLineNumber(content, asyncMatch.index),
139
- message: 'Async function with await calls but no try/catch error handling',
140
- recommendation: 'Wrap await calls in try/catch blocks to handle errors gracefully'
141
- });
142
- }
143
- }
144
-
145
- // Check for .then() without .catch()
146
- // Look for .then() and check if there's a .catch() later in the chain (not just immediately after)
147
- const thenPattern = /\.then\s*\([^)]*\)/g;
148
- let thenMatch;
149
- while ((thenMatch = thenPattern.exec(content)) !== null) {
150
- // Check if there's a .catch() after this .then() in the promise chain
151
- // Look ahead up to 500 chars (reasonable for a promise chain)
152
- const afterThen = content.substring(thenMatch.index, Math.min(content.length, thenMatch.index + 500));
153
-
154
- // Check if there's a .catch() in the chain
155
- // Also check for .then().catch() pattern
156
- const hasCatch = /\.catch\s*\(/.test(afterThen);
157
-
158
- // Check if the .then() handler has try/catch inside it (valid pattern: .then(async () => { try { ... } catch { ... } }))
159
- const hasTryCatchInHandler = /\.then\s*\(\s*async\s*\([^)]*\)\s*=>\s*\{[^}]*try\s*\{/.test(afterThen);
160
-
161
- // Also check if the .then() itself handles errors (returns error object)
162
- const thenBody = thenMatch[0];
163
- const handlesErrorInThen = /error|Error|catch/.test(thenBody);
164
-
165
- // Skip pace-core files - library code has complex patterns that are hard to detect accurately
166
- const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
167
-
168
- if (!hasCatch && !handlesErrorInThen && !hasTryCatchInHandler && !isPaceCorePackage) {
169
- warnings.push({
170
- type: 'unhandled-promise',
171
- file: relativePath,
172
- line: getLineNumber(content, thenMatch.index),
173
- message: 'Promise chain with .then() but no .catch() handler',
174
- recommendation: 'Add .catch() handler to handle promise rejections. The .catch() can come after the .then() in the chain.'
175
- });
176
- }
177
- }
178
-
179
- // Check for console.error without proper error handling
180
- const consoleErrorPattern = /console\.error\s*\([^)]*\)/g;
181
- let consoleMatch;
182
- while ((consoleMatch = consoleErrorPattern.exec(content)) !== null) {
183
- // Skip console.error in comments/JSDoc (example code)
184
- const beforeMatch = content.substring(Math.max(0, consoleMatch.index - 500), consoleMatch.index);
185
- const afterMatch = content.substring(consoleMatch.index, Math.min(content.length, consoleMatch.index + 50));
186
-
187
- // Get the line number to check if we're in a comment block
188
- const errorLineNum = getLineNumber(content, consoleMatch.index);
189
- const lines = content.split('\n');
190
- const errorLine = lines[errorLineNum - 1]; // Line numbers are 1-indexed
191
-
192
- // Check if the line starts with * (JSDoc comment block) or // (single line comment)
193
- const isInCommentLine = /^\s*\*|^\s*\/\//.test(errorLine);
194
-
195
- // Check if we're inside a /* ... */ or /** ... */ block
196
- // Find the last /* or /** before this position, and check if */ comes after
197
- const lastCommentStart = beforeMatch.lastIndexOf('/*');
198
- const lastCommentEndBefore = beforeMatch.lastIndexOf('*/');
199
- const firstCommentEndAfter = afterMatch.indexOf('*/');
200
- // We're in a comment block if there's a /* before and no */ before it (or */ is after)
201
- const isInCommentBlock = lastCommentStart !== -1 &&
202
- (lastCommentStart > lastCommentEndBefore || firstCommentEndAfter !== -1);
203
-
204
- // Check if it's in a comment (JSDoc example or comment block)
205
- if (isInCommentLine || isInCommentBlock) {
206
- continue; // Skip JSDoc examples and comment blocks
207
- }
208
-
209
- // Skip example files - these are demonstration code, not production code
210
- const isExampleFile = normalizedPath.includes('/examples/') || normalizedPath.includes('Example.tsx') || normalizedPath.includes('Example.ts');
211
- if (isExampleFile) {
212
- continue; // Skip example files
213
- }
214
-
215
- // Skip logger utility files - these ARE the error handling mechanism
216
- const isLoggerUtility = normalizedPath.includes('logger') ||
217
- normalizedPath.includes('debugLogger') ||
218
- normalizedPath.includes('rbac/config.ts'); // RBAC config is a logger setup
219
- if (isLoggerUtility) {
220
- continue; // Skip logger utilities
221
- }
222
-
223
- // Skip ESLint rule files - these are tooling, not application code
224
- const isEslintRule = normalizedPath.includes('eslint-rules') || normalizedPath.includes('eslint.config');
225
- if (isEslintRule) {
226
- continue; // Skip ESLint rules
227
- }
228
-
229
- // Skip performance monitoring utilities - these are informational logging
230
- const isPerformanceUtility = normalizedPath.includes('performance') &&
231
- (normalizedPath.includes('performanceBudgets') || normalizedPath.includes('performanceBenchmark'));
232
- if (isPerformanceUtility) {
233
- continue; // Skip performance monitoring
234
- }
235
-
236
- // Check if it's just logging without handling
237
- // Also check after the console.error to see if error state is set or error is returned
238
- const afterError = content.substring(consoleMatch.index, Math.min(content.length, consoleMatch.index + 200));
239
- const hasErrorHandling = beforeMatch.includes('throw') ||
240
- beforeMatch.includes('return') ||
241
- beforeMatch.includes('catch') ||
242
- afterError.includes('throw') || // Check after as well (e.g., console.error then throw)
243
- afterError.includes('setError') ||
244
- afterError.includes('error =') ||
245
- afterError.includes('onError') ||
246
- afterError.includes('onUploadError');
247
-
248
- if (!hasErrorHandling) {
249
- suggestions.push({
250
- type: 'error-logging-only',
251
- file: relativePath,
252
- line: getLineNumber(content, consoleMatch.index),
253
- message: 'Error is logged but may not be handled properly',
254
- recommendation: 'Ensure errors are properly handled (thrown, returned, or displayed to users)'
255
- });
256
- }
257
- }
258
-
259
- // Check for missing error states in components using pace-core hooks
260
- // Only check React component files
261
- if (filePath.match(/\.(tsx|jsx)$/)) {
262
- const paceCoreHooks = ['useUnifiedAuth', 'useOrganisations', 'useEvents', 'useSecureSupabase'];
263
- paceCoreHooks.forEach(hookName => {
264
- if (content.includes(hookName)) {
265
- const hookPattern = new RegExp(`const\\s+[^=]+=\\s+${hookName}\\s*\\(`, 'g');
266
- let hookMatch;
267
- while ((hookMatch = hookPattern.exec(content)) !== null) {
268
- // Check the entire component, not just 500 chars after hook
269
- // React's Rules of Hooks require all hooks to be called before conditional returns,
270
- // so error handling may occur later in the component
271
- const hookLine = getLineNumber(content, hookMatch.index);
272
- const lines = content.split('\n');
273
- const hookLineIndex = hookLine - 1;
274
-
275
- // Get the rest of the component after this hook
276
- const afterHookContent = lines.slice(hookLineIndex).join('\n');
277
-
278
- // Check if error is destructured from the hook
279
- const hookCallLine = lines[hookLineIndex];
280
- const hasErrorDestructured = /(error|Error|authError|orgError)/.test(hookCallLine);
281
-
282
- // For useSecureSupabase, check for null checks (it returns null on error)
283
- const isSecureSupabase = hookName === 'useSecureSupabase';
284
- const hasNullCheck = isSecureSupabase && /secureSupabase\s*===?\s*null|!secureSupabase/.test(afterHookContent);
285
-
286
- // Check for error handling patterns in the entire component:
287
- // 1. Component-level error checks with early returns: if (error) return <ErrorUI />
288
- // 2. useEffect error handlers: useEffect(() => { if (error) ... }, [error])
289
- // 3. Null checks for useSecureSupabase: if (secureSupabase === null) ...
290
- // 4. Error passed to error handling utilities
291
-
292
- const hasComponentLevelErrorCheck = /if\s*\(\s*(error|authError|orgError|rbacError)/.test(afterHookContent);
293
- const hasUseEffectErrorHandler = /useEffect\s*\([^)]*\([^)]*\)\s*=>\s*\{[^}]*\b(error|authError|orgError|rbacError)\b[^}]*\}/.test(afterHookContent);
294
- const hasErrorInDeps = /useEffect\s*\([^)]*,\s*\[[^\]]*\b(error|authError|orgError|rbacError)\b/.test(afterHookContent);
295
- const hasErrorHandling = /if\s*\(\s*(error|authError|orgError|rbacError)|(error|authError|orgError|rbacError)\s*\?|catch/.test(afterHookContent);
296
- const hasErrorUtility = /handleSupabaseError|handleError|toast.*error|showError/.test(afterHookContent);
297
-
298
- // Only flag if error is destructured but not handled anywhere
299
- if (hasErrorDestructured && !hasComponentLevelErrorCheck && !hasUseEffectErrorHandler &&
300
- !hasErrorInDeps && !hasErrorHandling && !hasErrorUtility && !hasNullCheck) {
301
- warnings.push({
302
- type: 'unhandled-hook-error',
303
- file: relativePath,
304
- line: hookLine,
305
- message: `${hookName} may return an error that is not handled`,
306
- recommendation: 'Check for error state and handle it appropriately. You can handle errors via: (1) Component-level checks after all hooks: if (error) return <ErrorUI />, (2) useEffect hooks that monitor error states, or (3) For useSecureSupabase, null checks: if (secureSupabase === null) ...'
307
- });
308
- }
309
- }
310
- }
311
- });
312
- }
313
- } catch (error) {
314
- // Skip files with errors
315
- }
316
- }
317
-
318
- // Check if ErrorBoundary is used in main app file
319
- // Skip this check if this is the pace-core repository (root src/ is a demo app)
320
- if (!hasErrorBoundary && !isPaceCoreRepository) {
321
- const mainFile = mainFiles.find(file => {
322
- const filePath = path.join(projectRoot, 'src', file);
323
- return fs.existsSync(filePath);
324
- });
325
-
326
- if (mainFile) {
327
- suggestions.push({
328
- type: 'missing-error-boundary',
329
- file: `src/${mainFile}`,
330
- message: 'No ErrorBoundary found in main app file',
331
- recommendation: 'Wrap your app with ErrorBoundary from @jmruthers/pace-core to catch and handle React errors gracefully'
332
- });
333
- }
334
- }
335
-
336
- return { issues, warnings, suggestions };
337
- }
338
- };
339
-
340
- module.exports = errorHandlingCheck;
@@ -1,172 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Form Validation Check Module
5
- * @package @jmruthers/pace-core
6
- * @module Audit/Checks/Forms
7
- *
8
- * Checks for:
9
- * - Missing form validation
10
- * - Incorrect useZodForm usage
11
- * - Missing error messages
12
- * - Form submission without loading states
13
- */
14
-
15
- const fs = require('fs');
16
- const { getRelativePath, getLineNumber } = require('../utils.cjs');
17
-
18
- const formsCheck = {
19
- name: 'forms',
20
- description: 'Form validation patterns (useZodForm, error handling)',
21
- severity: 'warning',
22
-
23
- async run(context) {
24
- const { projectRoot, files } = context;
25
- const issues = [];
26
- const warnings = [];
27
- const suggestions = [];
28
-
29
- if (!files || files.length === 0) {
30
- return { issues, warnings, suggestions };
31
- }
32
-
33
- for (const filePath of files) {
34
- try {
35
- // Only check React component files
36
- if (!filePath.match(/\.(tsx|jsx)$/)) {
37
- continue;
38
- }
39
-
40
- const content = fs.readFileSync(filePath, 'utf8');
41
- const relativePath = getRelativePath(filePath, projectRoot);
42
- const normalizedPath = relativePath.replace(/\\/g, '/');
43
-
44
- // Skip pace-core package files - forms check is for consuming applications, not the library itself
45
- // Note: Library components (like Form.tsx, LoginForm.tsx, Select.tsx) ARE the Form components
46
- // They don't need to use themselves - they provide the form functionality
47
- const isPaceCorePackage = normalizedPath.includes('packages/core/') || normalizedPath.startsWith('packages/core/');
48
- if (isPaceCorePackage) {
49
- continue; // Skip library files (including examples)
50
- }
51
-
52
- // Skip root-level src directory - in pace-core repository, this is a demo/showcase app
53
- const isRootSrc = normalizedPath.startsWith('src/') && !normalizedPath.includes('packages/');
54
- if (isRootSrc) {
55
- continue; // Skip demo app files
56
- }
57
-
58
- // Skip scripts directory - utility scripts don't need form validation
59
- const isScript = normalizedPath.startsWith('scripts/') || normalizedPath.includes('/scripts/');
60
- if (isScript) {
61
- continue; // Skip script files
62
- }
63
-
64
- // Check for form elements
65
- const hasForm = /<form|Form|useZodForm/.test(content);
66
- if (!hasForm) {
67
- continue;
68
- }
69
-
70
- // Check for useZodForm usage
71
- if (content.includes('useZodForm')) {
72
- const zodFormPattern = /const\s+[^=]+=\s+useZodForm\s*\(/g;
73
- let formMatch;
74
- while ((formMatch = zodFormPattern.exec(content)) !== null) {
75
- const afterForm = content.substring(formMatch.index, Math.min(content.length, formMatch.index + 500));
76
-
77
- // Check if schema is provided
78
- if (!afterForm.includes('schema:') && !afterForm.includes('schema =')) {
79
- warnings.push({
80
- type: 'missing-form-schema',
81
- file: relativePath,
82
- line: getLineNumber(content, formMatch.index),
83
- message: 'useZodForm called without schema',
84
- recommendation: 'Provide a Zod schema to useZodForm for form validation'
85
- });
86
- }
87
-
88
- // Check if form errors are displayed
89
- const hasErrorDisplay = /formState\.errors|errors\[|\.error/.test(content);
90
- if (!hasErrorDisplay) {
91
- suggestions.push({
92
- type: 'missing-form-errors',
93
- file: relativePath,
94
- line: getLineNumber(content, formMatch.index),
95
- message: 'Form may not be displaying validation errors',
96
- recommendation: 'Display form errors to users using formState.errors or FormField error prop'
97
- });
98
- }
99
- }
100
- }
101
-
102
- // Check for form submission without loading state
103
- const onSubmitPattern = /onSubmit\s*=\s*\{[^}]*async/g;
104
- let submitMatch;
105
- while ((submitMatch = onSubmitPattern.exec(content)) !== null) {
106
- const afterSubmit = content.substring(submitMatch.index, Math.min(content.length, submitMatch.index + 1000));
107
-
108
- // Check if there's a loading state
109
- const hasLoadingState = /isLoading|loading|isSubmitting|submitting/.test(afterSubmit);
110
- const hasDisabled = /disabled\s*=\s*\{/.test(content);
111
-
112
- if (!hasLoadingState && !hasDisabled) {
113
- suggestions.push({
114
- type: 'missing-submit-loading',
115
- file: relativePath,
116
- line: getLineNumber(content, submitMatch.index),
117
- message: 'Form submission without loading state',
118
- recommendation: 'Add loading state during form submission to prevent double-submission and provide user feedback'
119
- });
120
- }
121
- }
122
-
123
- // Check for native form elements that should use pace-core Form
124
- const nativeFormPattern = /<form[^>]*>/g;
125
- let nativeMatch;
126
- while ((nativeMatch = nativeFormPattern.exec(content)) !== null) {
127
- const beforeMatch = content.substring(Math.max(0, nativeMatch.index - 100), nativeMatch.index);
128
- const usesPaceCoreForm = beforeMatch.includes('from \'@jmruthers/pace-core\'') ||
129
- content.includes('<Form');
130
-
131
- if (!usesPaceCoreForm) {
132
- suggestions.push({
133
- type: 'native-form-element',
134
- file: relativePath,
135
- line: getLineNumber(content, nativeMatch.index),
136
- message: 'Native <form> element detected',
137
- recommendation: 'Use Form component from @jmruthers/pace-core for consistent styling and validation'
138
- });
139
- }
140
- }
141
-
142
- // Check for input elements without validation
143
- const inputPattern = /<input[^>]*>/g;
144
- let inputMatch;
145
- while ((inputMatch = inputPattern.exec(content)) !== null) {
146
- const inputTag = inputMatch[0];
147
- const hasFormField = content.substring(Math.max(0, inputMatch.index - 200), inputMatch.index).includes('FormField');
148
- const hasValidation = inputTag.includes('required') ||
149
- inputTag.includes('pattern') ||
150
- content.substring(Math.max(0, inputMatch.index - 200), inputMatch.index).includes('register');
151
-
152
- if (!hasFormField && !hasValidation && !inputTag.includes('type="hidden"')) {
153
- suggestions.push({
154
- type: 'unvalidated-input',
155
- file: relativePath,
156
- line: getLineNumber(content, inputMatch.index),
157
- message: 'Input element without validation',
158
- recommendation: 'Use FormField from @jmruthers/pace-core or add validation to form inputs'
159
- });
160
- }
161
- }
162
-
163
- } catch (error) {
164
- // Skip files with errors
165
- }
166
- }
167
-
168
- return { issues, warnings, suggestions };
169
- }
170
- };
171
-
172
- module.exports = formsCheck;
@@ -1,68 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Heuristic Checks Module
5
- * @package @jmruthers/pace-core
6
- * @module Audit/Checks/Heuristics
7
- *
8
- * Code quality heuristics including:
9
- * - Large files
10
- * - God objects (files with many exports)
11
- */
12
-
13
- const fs = require('fs');
14
- const path = require('path');
15
-
16
- const heuristicsCheck = {
17
- name: 'heuristics',
18
- description: 'Code quality heuristics (large files, god objects)',
19
- severity: 'suggestion',
20
-
21
- async run(context) {
22
- const { projectRoot, files } = context;
23
- const suggestions = [];
24
-
25
- if (!files || files.length === 0) {
26
- return { issues: [], warnings: [], suggestions: [] };
27
- }
28
-
29
- const maxLines = 1000;
30
-
31
- // Check for large files
32
- for (const filePath of files) {
33
- try {
34
- const content = fs.readFileSync(filePath, 'utf8');
35
- const lines = content.split('\n').length;
36
-
37
- if (lines > maxLines) {
38
- const relativePath = path.relative(projectRoot, filePath);
39
- suggestions.push({
40
- type: 'large-file',
41
- file: relativePath,
42
- message: `File has ${lines} lines, consider splitting into smaller modules`,
43
- recommendation: `Split this file into smaller, focused modules (target: <${maxLines} lines)`
44
- });
45
- }
46
-
47
- // Check for god objects (files with many exports)
48
- const exportCount = (content.match(/export\s+(const|function|class|interface|type)/g) || []).length;
49
-
50
- if (exportCount > 10) {
51
- const relativePath = path.relative(projectRoot, filePath);
52
- suggestions.push({
53
- type: 'god-object',
54
- file: relativePath,
55
- message: `File exports ${exportCount} items, consider splitting into smaller modules`,
56
- recommendation: `Split this file into smaller modules, each with a focused responsibility (target: <10 exports per file)`
57
- });
58
- }
59
- } catch (error) {
60
- // Skip files we can't read
61
- }
62
- }
63
-
64
- return { issues: [], warnings: [], suggestions };
65
- }
66
- };
67
-
68
- module.exports = heuristicsCheck;